Bug 1821972 part 2: Remove ipc/mscom code only used by the old accessibility architecture based on content process COM proxies. r=bobowen

Nothing else uses any of this or is likely to use it, so I don't think it makes sense to leave it as dead code.

Differential Revision: https://phabricator.services.mozilla.com/D177967
This commit is contained in:
James Teh 2023-05-22 20:19:46 +00:00
parent f731ca6503
commit e4c5cc5d7a
53 changed files with 8 additions and 9104 deletions

View File

@ -601,12 +601,7 @@ ContentChild* ContentChild::sSingleton;
StaticAutoPtr<ContentChild::ShutdownCanary> ContentChild::sShutdownCanary;
ContentChild::ContentChild()
: mID(uint64_t(-1))
#if defined(XP_WIN) && defined(ACCESSIBILITY)
,
mMainChromeTid(0)
#endif
,
: mID(uint64_t(-1)),
mIsForBrowser(false),
mIsAlive(true),
mShuttingDown(false) {
@ -2548,14 +2543,8 @@ mozilla::ipc::IPCResult ContentChild::RecvFlushMemory(const nsString& reason) {
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvActivateA11y(
const uint32_t& aMainChromeTid) {
mozilla::ipc::IPCResult ContentChild::RecvActivateA11y() {
#ifdef ACCESSIBILITY
# ifdef XP_WIN
MOZ_ASSERT(aMainChromeTid != 0);
mMainChromeTid = aMainChromeTid;
# endif // XP_WIN
// Start accessibility in content process if it's running in chrome
// process.
GetOrCreateAccService(nsAccessibilityService::eMainProcess);

View File

@ -346,7 +346,7 @@ class ContentChild final : public PContentChild,
mozilla::ipc::IPCResult RecvFlushMemory(const nsString& reason);
mozilla::ipc::IPCResult RecvActivateA11y(const uint32_t& aMainChromeTid);
mozilla::ipc::IPCResult RecvActivateA11y();
mozilla::ipc::IPCResult RecvShutdownA11y();
mozilla::ipc::IPCResult RecvApplicationForeground();
@ -456,10 +456,6 @@ class ContentChild final : public PContentChild,
ContentParentId GetID() const { return mID; }
#if defined(XP_WIN) && defined(ACCESSIBILITY)
uint32_t GetChromeMainThreadId() const { return mMainChromeTid; }
#endif
bool IsForBrowser() const { return mIsForBrowser; }
MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvConstructBrowser(
@ -841,13 +837,6 @@ class ContentChild final : public PContentChild,
*/
ContentParentId mID;
#if defined(XP_WIN) && defined(ACCESSIBILITY)
/**
* The thread ID of the main thread in the chrome process.
*/
uint32_t mMainChromeTid;
#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
AppInfo mAppInfo;
bool mIsForBrowser;

View File

@ -1687,10 +1687,10 @@ void ContentParent::Init() {
# if defined(XP_WIN)
// Don't init content a11y if we detect an incompat version of JAWS in use.
if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
Unused << SendActivateA11y(::GetCurrentThreadId());
Unused << SendActivateA11y();
}
# else
Unused << SendActivateA11y(0);
Unused << SendActivateA11y();
# endif
}
#endif // #ifdef ACCESSIBILITY
@ -4051,10 +4051,10 @@ ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
// Don't init content a11y if we detect an incompat version of JAWS in
// use.
if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
Unused << SendActivateA11y(::GetCurrentThreadId());
Unused << SendActivateA11y();
}
# else
Unused << SendActivateA11y(0);
Unused << SendActivateA11y();
# endif
} else {
// If possible, shut down accessibility in content process when

View File

@ -751,10 +751,8 @@ child:
/**
* Start accessibility engine in content process.
* @param aTid is the thread ID of the chrome process main thread. Only used
* on Windows; pass 0 on other platforms.
*/
async ActivateA11y(uint32_t aMainChromeTid);
async ActivateA11y();
/**
* Shutdown accessibility engine in content process (if not in use).

View File

@ -1,223 +0,0 @@
/* -*- 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"
#include "mozilla/mscom/Utils.h"
namespace mozilla {
namespace mscom {
ActivationContext::ActivationContext(ActCtxResource aResource)
: ActivationContext(aResource.mModule, aResource.mId) {}
ActivationContext::ActivationContext(HMODULE aLoadFromModule, WORD aResourceId)
: mActCtx(INVALID_HANDLE_VALUE) {
ACTCTXW actCtx = {sizeof(actCtx)};
actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_HMODULE_VALID;
actCtx.lpResourceName = MAKEINTRESOURCEW(aResourceId);
actCtx.hModule = aLoadFromModule;
Init(actCtx);
}
void ActivationContext::Init(ACTCTXW& aActCtx) {
MOZ_ASSERT(mActCtx == INVALID_HANDLE_VALUE);
mActCtx = ::CreateActCtxW(&aActCtx);
MOZ_ASSERT(mActCtx != INVALID_HANDLE_VALUE);
}
void ActivationContext::AddRef() {
if (mActCtx == INVALID_HANDLE_VALUE) {
return;
}
::AddRefActCtx(mActCtx);
}
ActivationContext::ActivationContext(ActivationContext&& aOther)
: mActCtx(aOther.mActCtx) {
aOther.mActCtx = INVALID_HANDLE_VALUE;
}
ActivationContext& ActivationContext::operator=(ActivationContext&& aOther) {
Release();
mActCtx = aOther.mActCtx;
aOther.mActCtx = INVALID_HANDLE_VALUE;
return *this;
}
ActivationContext::ActivationContext(const ActivationContext& aOther)
: mActCtx(aOther.mActCtx) {
AddRef();
}
ActivationContext& ActivationContext::operator=(
const ActivationContext& aOther) {
Release();
mActCtx = aOther.mActCtx;
AddRef();
return *this;
}
void ActivationContext::Release() {
if (mActCtx == INVALID_HANDLE_VALUE) {
return;
}
::ReleaseActCtx(mActCtx);
mActCtx = INVALID_HANDLE_VALUE;
}
ActivationContext::~ActivationContext() { Release(); }
#if defined(MOZILLA_INTERNAL_API)
/* static */ Result<uintptr_t, HRESULT> ActivationContext::GetCurrent() {
HANDLE actCtx;
if (!::GetCurrentActCtx(&actCtx)) {
return Result<uintptr_t, HRESULT>(HRESULT_FROM_WIN32(::GetLastError()));
}
return reinterpret_cast<uintptr_t>(actCtx);
}
/* static */
HRESULT ActivationContext::GetCurrentManifestPath(nsAString& aOutManifestPath) {
aOutManifestPath.Truncate();
SIZE_T bytesNeeded;
BOOL ok = ::QueryActCtxW(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr,
nullptr, ActivationContextDetailedInformation,
nullptr, 0, &bytesNeeded);
if (!ok) {
DWORD err = ::GetLastError();
if (err != ERROR_INSUFFICIENT_BUFFER) {
return HRESULT_FROM_WIN32(err);
}
}
auto ctxBuf = MakeUnique<BYTE[]>(bytesNeeded);
ok = ::QueryActCtxW(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr, nullptr,
ActivationContextDetailedInformation, ctxBuf.get(),
bytesNeeded, nullptr);
if (!ok) {
return HRESULT_FROM_WIN32(::GetLastError());
}
auto ctxInfo =
reinterpret_cast<ACTIVATION_CONTEXT_DETAILED_INFORMATION*>(ctxBuf.get());
// assemblyIndex is 1-based, and we want the last index, so we can just copy
// ctxInfo->ulAssemblyCount directly.
DWORD assemblyIndex = ctxInfo->ulAssemblyCount;
ok = ::QueryActCtxW(
QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr, &assemblyIndex,
AssemblyDetailedInformationInActivationContext, nullptr, 0, &bytesNeeded);
if (!ok) {
DWORD err = ::GetLastError();
if (err != ERROR_INSUFFICIENT_BUFFER) {
return HRESULT_FROM_WIN32(err);
}
}
auto assemblyBuf = MakeUnique<BYTE[]>(bytesNeeded);
ok = ::QueryActCtxW(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr,
&assemblyIndex,
AssemblyDetailedInformationInActivationContext,
assemblyBuf.get(), bytesNeeded, &bytesNeeded);
if (!ok) {
return HRESULT_FROM_WIN32(::GetLastError());
}
auto assemblyInfo =
reinterpret_cast<ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION*>(
assemblyBuf.get());
aOutManifestPath = nsDependentString(
assemblyInfo->lpAssemblyManifestPath,
(assemblyInfo->ulManifestPathLength + 1) / sizeof(wchar_t));
return S_OK;
}
#endif // defined(MOZILLA_INTERNAL_API)
ActivationContextRegion::ActivationContextRegion() : mActCookie(0) {}
ActivationContextRegion::ActivationContextRegion(
const ActivationContext& aActCtx)
: mActCtx(aActCtx), mActCookie(0) {
Activate();
}
ActivationContextRegion& ActivationContextRegion::operator=(
const ActivationContext& aActCtx) {
Deactivate();
mActCtx = aActCtx;
Activate();
return *this;
}
ActivationContextRegion::ActivationContextRegion(ActivationContext&& aActCtx)
: mActCtx(std::move(aActCtx)), mActCookie(0) {
Activate();
}
ActivationContextRegion& ActivationContextRegion::operator=(
ActivationContext&& aActCtx) {
Deactivate();
mActCtx = std::move(aActCtx);
Activate();
return *this;
}
ActivationContextRegion::ActivationContextRegion(ActivationContextRegion&& aRgn)
: mActCtx(std::move(aRgn.mActCtx)), mActCookie(aRgn.mActCookie) {
aRgn.mActCookie = 0;
}
ActivationContextRegion& ActivationContextRegion::operator=(
ActivationContextRegion&& aRgn) {
Deactivate();
mActCtx = std::move(aRgn.mActCtx);
mActCookie = aRgn.mActCookie;
aRgn.mActCookie = 0;
return *this;
}
void ActivationContextRegion::Activate() {
if (mActCtx.mActCtx == INVALID_HANDLE_VALUE) {
return;
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
BOOL activated =
#endif
::ActivateActCtx(mActCtx.mActCtx, &mActCookie);
MOZ_DIAGNOSTIC_ASSERT(activated);
}
bool ActivationContextRegion::Deactivate() {
if (!mActCookie) {
return true;
}
BOOL deactivated = ::DeactivateActCtx(0, mActCookie);
MOZ_DIAGNOSTIC_ASSERT(deactivated);
if (deactivated) {
mActCookie = 0;
}
return !!deactivated;
}
ActivationContextRegion::~ActivationContextRegion() { Deactivate(); }
} // namespace mscom
} // namespace mozilla

View File

@ -1,101 +0,0 @@
/* -*- 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 <utility>
#include "mozilla/Attributes.h"
#include "mozilla/mscom/ActCtxResource.h"
#if defined(MOZILLA_INTERNAL_API)
# include "mozilla/ResultVariant.h"
# include "nsString.h"
#endif // defined(MOZILLA_INTERNAL_API)
#include <windows.h>
namespace mozilla {
namespace mscom {
class ActivationContext final {
public:
// This is the default resource ID that the Windows dynamic linker searches
// for when seeking a manifest while loading a DLL.
static constexpr WORD kDllManifestDefaultResourceId = 2;
ActivationContext() : mActCtx(INVALID_HANDLE_VALUE) {}
explicit ActivationContext(ActCtxResource aResource);
explicit ActivationContext(HMODULE aLoadFromModule,
WORD aResourceId = kDllManifestDefaultResourceId);
ActivationContext(ActivationContext&& aOther);
ActivationContext& operator=(ActivationContext&& aOther);
ActivationContext(const ActivationContext& aOther);
ActivationContext& operator=(const ActivationContext& aOther);
~ActivationContext();
explicit operator bool() const { return mActCtx != INVALID_HANDLE_VALUE; }
#if defined(MOZILLA_INTERNAL_API)
static Result<uintptr_t, HRESULT> GetCurrent();
static HRESULT GetCurrentManifestPath(nsAString& aOutManifestPath);
#endif // defined(MOZILLA_INTERNAL_API)
private:
void Init(ACTCTXW& aActCtx);
void AddRef();
void Release();
private:
HANDLE mActCtx;
friend class ActivationContextRegion;
};
class MOZ_NON_TEMPORARY_CLASS ActivationContextRegion final {
public:
template <typename... Args>
explicit ActivationContextRegion(Args&&... aArgs)
: mActCtx(std::forward<Args>(aArgs)...), mActCookie(0) {
Activate();
}
ActivationContextRegion();
explicit ActivationContextRegion(const ActivationContext& aActCtx);
ActivationContextRegion& operator=(const ActivationContext& aActCtx);
explicit ActivationContextRegion(ActivationContext&& aActCtx);
ActivationContextRegion& operator=(ActivationContext&& aActCtx);
ActivationContextRegion(ActivationContextRegion&& aRgn);
ActivationContextRegion& operator=(ActivationContextRegion&& aRgn);
~ActivationContextRegion();
explicit operator bool() const { return !!mActCookie; }
ActivationContextRegion(const ActivationContextRegion&) = delete;
ActivationContextRegion& operator=(const ActivationContextRegion&) = delete;
bool Deactivate();
private:
void Activate();
ActivationContext mActCtx;
ULONG_PTR mActCookie;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_ActivationContext_h

View File

@ -1,465 +0,0 @@
/* -*- 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_AsyncInvoker_h
#define mozilla_mscom_AsyncInvoker_h
#include <objidl.h>
#include <windows.h>
#include <utility>
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/mscom/Aggregation.h"
#include "mozilla/mscom/Utils.h"
#include "nsISerialEventTarget.h"
#include "nsISupportsImpl.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace mscom {
namespace detail {
template <typename AsyncInterface>
class ForgettableAsyncCall : public ISynchronize {
public:
explicit ForgettableAsyncCall(ICallFactory* aCallFactory)
: mRefCnt(0), mAsyncCall(nullptr) {
StabilizedRefCount<Atomic<ULONG>> stabilizer(mRefCnt);
HRESULT hr =
aCallFactory->CreateCall(__uuidof(AsyncInterface), this, IID_IUnknown,
getter_AddRefs(mInnerUnk));
if (FAILED(hr)) {
return;
}
hr = mInnerUnk->QueryInterface(__uuidof(AsyncInterface),
reinterpret_cast<void**>(&mAsyncCall));
if (SUCCEEDED(hr)) {
// Don't hang onto a ref. Because mAsyncCall is aggregated, its refcount
// is this->mRefCnt, so we'd create a cycle!
mAsyncCall->Release();
}
}
AsyncInterface* GetInterface() const { return mAsyncCall; }
// IUnknown
STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) final {
if (aIid == IID_ISynchronize || aIid == IID_IUnknown) {
RefPtr<ISynchronize> ptr(this);
ptr.forget(aOutInterface);
return S_OK;
}
return mInnerUnk->QueryInterface(aIid, aOutInterface);
}
STDMETHODIMP_(ULONG) AddRef() final {
ULONG result = ++mRefCnt;
NS_LOG_ADDREF(this, result, "ForgettableAsyncCall", sizeof(*this));
return result;
}
STDMETHODIMP_(ULONG) Release() final {
ULONG result = --mRefCnt;
NS_LOG_RELEASE(this, result, "ForgettableAsyncCall");
if (!result) {
delete this;
}
return result;
}
// ISynchronize
STDMETHODIMP Wait(DWORD aFlags, DWORD aTimeoutMilliseconds) override {
return E_NOTIMPL;
}
STDMETHODIMP Signal() override {
// Even though this function is a no-op, we must return S_OK as opposed to
// E_NOTIMPL or else COM will consider the async call to have failed.
return S_OK;
}
STDMETHODIMP Reset() override {
// Even though this function is a no-op, we must return S_OK as opposed to
// E_NOTIMPL or else COM will consider the async call to have failed.
return S_OK;
}
protected:
virtual ~ForgettableAsyncCall() = default;
private:
Atomic<ULONG> mRefCnt;
RefPtr<IUnknown> mInnerUnk;
AsyncInterface* mAsyncCall; // weak reference
};
template <typename AsyncInterface>
class WaitableAsyncCall : public ForgettableAsyncCall<AsyncInterface> {
public:
explicit WaitableAsyncCall(ICallFactory* aCallFactory)
: ForgettableAsyncCall<AsyncInterface>(aCallFactory),
mEvent(::CreateEventW(nullptr, FALSE, FALSE, nullptr)) {}
STDMETHODIMP Wait(DWORD aFlags, DWORD aTimeoutMilliseconds) override {
const DWORD waitStart =
aTimeoutMilliseconds == INFINITE ? 0 : ::GetTickCount();
DWORD flags = aFlags;
if (XRE_IsContentProcess() && NS_IsMainThread()) {
flags |= COWAIT_ALERTABLE;
}
HRESULT hr;
DWORD signaledIdx;
DWORD elapsed = 0;
while (true) {
if (aTimeoutMilliseconds != INFINITE) {
elapsed = ::GetTickCount() - waitStart;
}
if (elapsed >= aTimeoutMilliseconds) {
return RPC_S_CALLPENDING;
}
::SetLastError(ERROR_SUCCESS);
hr = ::CoWaitForMultipleHandles(flags, aTimeoutMilliseconds - elapsed, 1,
&mEvent, &signaledIdx);
if (hr == RPC_S_CALLPENDING || FAILED(hr)) {
return hr;
}
if (hr == S_OK && signaledIdx == 0) {
return hr;
}
}
}
STDMETHODIMP Signal() override {
if (!::SetEvent(mEvent)) {
return HRESULT_FROM_WIN32(::GetLastError());
}
return S_OK;
}
protected:
~WaitableAsyncCall() {
if (mEvent) {
::CloseHandle(mEvent);
}
}
private:
HANDLE mEvent;
};
template <typename AsyncInterface>
class EventDrivenAsyncCall : public ForgettableAsyncCall<AsyncInterface> {
public:
explicit EventDrivenAsyncCall(ICallFactory* aCallFactory)
: ForgettableAsyncCall<AsyncInterface>(aCallFactory) {}
bool HasCompletionRunnable() const { return !!mCompletionRunnable; }
void ClearCompletionRunnable() { mCompletionRunnable = nullptr; }
void SetCompletionRunnable(already_AddRefed<nsIRunnable> aRunnable) {
nsCOMPtr<nsIRunnable> innerRunnable(aRunnable);
MOZ_ASSERT(!!innerRunnable);
if (!innerRunnable) {
return;
}
// We need to retain a ref to ourselves to outlive the AsyncInvoker
// such that our callback can execute.
RefPtr<EventDrivenAsyncCall<AsyncInterface>> self(this);
mCompletionRunnable = NS_NewRunnableFunction(
"EventDrivenAsyncCall outer completion Runnable",
[innerRunnable = std::move(innerRunnable), self = std::move(self)]() {
innerRunnable->Run();
});
}
void SetEventTarget(nsISerialEventTarget* aTarget) { mEventTarget = aTarget; }
STDMETHODIMP Signal() override {
MOZ_ASSERT(!!mCompletionRunnable);
if (!mCompletionRunnable) {
return S_OK;
}
nsCOMPtr<nsISerialEventTarget> eventTarget(mEventTarget.forget());
if (!eventTarget) {
eventTarget = GetMainThreadSerialEventTarget();
}
DebugOnly<nsresult> rv =
eventTarget->Dispatch(mCompletionRunnable.forget(), NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return S_OK;
}
private:
nsCOMPtr<nsIRunnable> mCompletionRunnable;
nsCOMPtr<nsISerialEventTarget> mEventTarget;
};
template <typename AsyncInterface>
class FireAndForgetInvoker {
protected:
void OnBeginInvoke() {}
void OnSyncInvoke(HRESULT aHr) {}
void OnAsyncInvokeFailed() {}
typedef ForgettableAsyncCall<AsyncInterface> AsyncCallType;
RefPtr<ForgettableAsyncCall<AsyncInterface>> mAsyncCall;
};
template <typename AsyncInterface>
class WaitableInvoker {
public:
HRESULT Wait(DWORD aTimeout = INFINITE) const {
if (!mAsyncCall) {
// Nothing to wait for
return S_OK;
}
return mAsyncCall->Wait(0, aTimeout);
}
protected:
void OnBeginInvoke() {}
void OnSyncInvoke(HRESULT aHr) {}
void OnAsyncInvokeFailed() {}
typedef WaitableAsyncCall<AsyncInterface> AsyncCallType;
RefPtr<WaitableAsyncCall<AsyncInterface>> mAsyncCall;
};
template <typename AsyncInterface>
class EventDrivenInvoker {
public:
void SetCompletionRunnable(already_AddRefed<nsIRunnable> aRunnable) {
if (mAsyncCall) {
mAsyncCall->SetCompletionRunnable(std::move(aRunnable));
return;
}
mCompletionRunnable = aRunnable;
}
void SetAsyncEventTarget(nsISerialEventTarget* aTarget) {
if (mAsyncCall) {
mAsyncCall->SetEventTarget(aTarget);
}
}
protected:
void OnBeginInvoke() {
MOZ_RELEASE_ASSERT(
mCompletionRunnable ||
(mAsyncCall && mAsyncCall->HasCompletionRunnable()),
"You should have called SetCompletionRunnable before invoking!");
}
void OnSyncInvoke(HRESULT aHr) {
nsCOMPtr<nsIRunnable> completionRunnable(mCompletionRunnable.forget());
if (FAILED(aHr)) {
return;
}
completionRunnable->Run();
}
void OnAsyncInvokeFailed() {
MOZ_ASSERT(!!mAsyncCall);
mAsyncCall->ClearCompletionRunnable();
}
typedef EventDrivenAsyncCall<AsyncInterface> AsyncCallType;
RefPtr<EventDrivenAsyncCall<AsyncInterface>> mAsyncCall;
nsCOMPtr<nsIRunnable> mCompletionRunnable;
};
} // namespace detail
/**
* This class is intended for "fire-and-forget" asynchronous invocations of COM
* interfaces. This requires that an interface be annotated with the
* |async_uuid| attribute in midl. We also require that there be no outparams
* in the desired asynchronous interface (otherwise that would break the
* desired "fire-and-forget" semantics).
*
* For example, let us suppose we have some IDL as such:
* [object, uuid(...), async_uuid(...)]
* interface IFoo : IUnknown
* {
* HRESULT Bar([in] long baz);
* }
*
* Then, given an IFoo, we may construct an AsyncInvoker<IFoo, AsyncIFoo>:
*
* IFoo* foo = ...;
* AsyncInvoker<IFoo, AsyncIFoo> myInvoker(foo);
* HRESULT hr = myInvoker.Invoke(&IFoo::Bar, &AsyncIFoo::Begin_Bar, 7);
*
* Alternatively you may use the ASYNC_INVOKER_FOR and ASYNC_INVOKE macros,
* which automatically deduce the name of the asynchronous interface from the
* name of the synchronous interface:
*
* ASYNC_INVOKER_FOR(IFoo) myInvoker(foo);
* HRESULT hr = ASYNC_INVOKE(myInvoker, Bar, 7);
*
* This class may also be used when a synchronous COM call must be made that
* might reenter the content process. In this case, use the WaitableAsyncInvoker
* variant, or the WAITABLE_ASYNC_INVOKER_FOR macro:
*
* WAITABLE_ASYNC_INVOKER_FOR(Ifoo) myInvoker(foo);
* HRESULT hr = ASYNC_INVOKE(myInvoker, Bar, 7);
* if (SUCCEEDED(hr)) {
* myInvoker.Wait(); // <-- Wait for the COM call to complete.
* }
*
* In general you should avoid using the waitable version, but in some corner
* cases it is absolutely necessary in order to preserve correctness while
* avoiding deadlock.
*
* Finally, it is also possible to have the async invoker enqueue a runnable
* to the main thread when the async operation completes:
*
* EVENT_DRIVEN_ASYNC_INVOKER_FOR(Ifoo) myInvoker(foo);
* // myRunnable will be invoked on the main thread once the async operation
* // has completed. Note that we set this *before* we do the ASYNC_INVOKE!
* myInvoker.SetCompletionRunnable(myRunnable.forget());
* HRESULT hr = ASYNC_INVOKE(myInvoker, Bar, 7);
* // ...
*/
template <typename SyncInterface, typename AsyncInterface,
template <typename Iface> class WaitPolicy =
detail::FireAndForgetInvoker>
class MOZ_RAII AsyncInvoker final : public WaitPolicy<AsyncInterface> {
using Base = WaitPolicy<AsyncInterface>;
public:
typedef SyncInterface SyncInterfaceT;
typedef AsyncInterface AsyncInterfaceT;
/**
* @param aSyncObj The COM object on which to invoke the asynchronous event.
* If this object is not a proxy to the synchronous variant
* of AsyncInterface, then it will be invoked synchronously
* instead (because it is an in-process virtual method call).
* @param aIsProxy An optional hint as to whether or not aSyncObj is a proxy.
* If not specified, AsyncInvoker will automatically detect
* whether aSyncObj is a proxy, however there may be a
* performance penalty associated with that.
*/
explicit AsyncInvoker(SyncInterface* aSyncObj,
const Maybe<bool>& aIsProxy = Nothing()) {
MOZ_ASSERT(aSyncObj);
RefPtr<ICallFactory> callFactory;
if ((aIsProxy.isSome() && !aIsProxy.value()) ||
FAILED(aSyncObj->QueryInterface(IID_ICallFactory,
getter_AddRefs(callFactory)))) {
mSyncObj = aSyncObj;
return;
}
this->mAsyncCall = new typename Base::AsyncCallType(callFactory);
}
/**
* @brief Invoke a method on the object. Member function pointers are provided
* for both the sychronous and asynchronous variants of the interface.
* If this invoker's encapsulated COM object is a proxy, then Invoke
* will call the asynchronous member function. Otherwise the
* synchronous version must be used, as the invocation will simply be a
* virtual function call that executes in-process.
* @param aSyncMethod Pointer to the method that we would like to invoke on
* the synchronous interface.
* @param aAsyncMethod Pointer to the method that we would like to invoke on
* the asynchronous interface.
*/
template <typename SyncMethod, typename AsyncMethod, typename... Args>
HRESULT Invoke(SyncMethod aSyncMethod, AsyncMethod aAsyncMethod,
Args&&... aArgs) {
this->OnBeginInvoke();
if (mSyncObj) {
HRESULT hr = (mSyncObj->*aSyncMethod)(std::forward<Args>(aArgs)...);
this->OnSyncInvoke(hr);
return hr;
}
MOZ_ASSERT(this->mAsyncCall);
if (!this->mAsyncCall) {
this->OnAsyncInvokeFailed();
return E_POINTER;
}
AsyncInterface* asyncInterface = this->mAsyncCall->GetInterface();
MOZ_ASSERT(asyncInterface);
if (!asyncInterface) {
this->OnAsyncInvokeFailed();
return E_POINTER;
}
HRESULT hr = (asyncInterface->*aAsyncMethod)(std::forward<Args>(aArgs)...);
if (FAILED(hr)) {
this->OnAsyncInvokeFailed();
}
return hr;
}
AsyncInvoker(const AsyncInvoker& aOther) = delete;
AsyncInvoker(AsyncInvoker&& aOther) = delete;
AsyncInvoker& operator=(const AsyncInvoker& aOther) = delete;
AsyncInvoker& operator=(AsyncInvoker&& aOther) = delete;
private:
RefPtr<SyncInterface> mSyncObj;
};
template <typename SyncInterface, typename AsyncInterface>
using WaitableAsyncInvoker =
AsyncInvoker<SyncInterface, AsyncInterface, detail::WaitableInvoker>;
template <typename SyncInterface, typename AsyncInterface>
using EventDrivenAsyncInvoker =
AsyncInvoker<SyncInterface, AsyncInterface, detail::EventDrivenInvoker>;
} // namespace mscom
} // namespace mozilla
#define ASYNC_INVOKER_FOR(SyncIface) \
mozilla::mscom::AsyncInvoker<SyncIface, Async##SyncIface>
#define WAITABLE_ASYNC_INVOKER_FOR(SyncIface) \
mozilla::mscom::WaitableAsyncInvoker<SyncIface, Async##SyncIface>
#define EVENT_DRIVEN_ASYNC_INVOKER_FOR(SyncIface) \
mozilla::mscom::EventDrivenAsyncInvoker<SyncIface, Async##SyncIface>
#define ASYNC_INVOKE(InvokerObj, SyncMethodName, ...) \
InvokerObj.Invoke( \
&decltype(InvokerObj)::SyncInterfaceT::SyncMethodName, \
&decltype(InvokerObj)::AsyncInterfaceT::Begin_##SyncMethodName, \
##__VA_ARGS__)
#endif // mozilla_mscom_AsyncInvoker_h

View File

@ -1,201 +0,0 @@
/* -*- 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 <utility>
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/mscom/ProxyStream.h"
#include "mozilla/mscom/Ptr.h"
#if defined(MOZ_SANDBOX)
# include "mozilla/SandboxSettings.h"
#endif // defined(MOZ_SANDBOX)
#include "nsExceptionHandler.h"
namespace mozilla {
namespace mscom {
template <typename Interface, const IID& _IID>
class COMPtrHolder {
public:
typedef ProxyUniquePtr<Interface> COMPtrType;
typedef COMPtrHolder<Interface, _IID> ThisType;
typedef typename detail::EnvironmentSelector<Interface>::Type EnvType;
COMPtrHolder() {}
MOZ_IMPLICIT COMPtrHolder(decltype(nullptr)) {}
explicit COMPtrHolder(COMPtrType&& aPtr)
: mPtr(std::forward<COMPtrType>(aPtr)) {}
COMPtrHolder(COMPtrType&& aPtr, const ActivationContext& aActCtx)
: mPtr(std::forward<COMPtrType>(aPtr)), mActCtx(aActCtx) {}
Interface* Get() const { return mPtr.get(); }
[[nodiscard]] Interface* Release() { return mPtr.release(); }
void Set(COMPtrType&& aPtr) { mPtr = std::forward<COMPtrType>(aPtr); }
void SetActCtx(const ActivationContext& aActCtx) { mActCtx = aActCtx; }
#if defined(MOZ_SANDBOX)
// This method is const because we need to call it during IPC write, where
// we are passed as a const argument. At higher sandboxing levels we need to
// save this artifact from the serialization process for later deletion.
void PreserveStream(PreservedStreamPtr aPtr) const {
MOZ_ASSERT(!mMarshaledStream);
mMarshaledStream = std::move(aPtr);
}
PreservedStreamPtr GetPreservedStream() {
return std::move(mMarshaledStream);
}
#endif // defined(MOZ_SANDBOX)
COMPtrHolder(const COMPtrHolder& aOther) = delete;
COMPtrHolder(COMPtrHolder&& aOther)
: mPtr(std::move(aOther.mPtr))
#if defined(MOZ_SANDBOX)
,
mMarshaledStream(std::move(aOther.mMarshaledStream))
#endif // defined(MOZ_SANDBOX)
{
}
// 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(std::move(aOther.mPtr));
#if defined(MOZ_SANDBOX)
mMarshaledStream = std::move(aOther.mMarshaledStream);
#endif // defined(MOZ_SANDBOX)
return *this;
}
ThisType& operator=(ThisType&& aOther) {
Set(std::move(aOther.mPtr));
#if defined(MOZ_SANDBOX)
mMarshaledStream = std::move(aOther.mMarshaledStream);
#endif // defined(MOZ_SANDBOX)
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;
ActivationContext mActCtx;
#if defined(MOZ_SANDBOX)
// This is mutable so that we may optionally store a reference to a marshaled
// stream to be cleaned up later via PreserveStream().
mutable PreservedStreamPtr mMarshaledStream;
#endif // defined(MOZ_SANDBOX)
};
} // 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(MessageWriter* aWriter, const paramType& aParam) {
#if defined(MOZ_SANDBOX)
static const bool sIsStreamPreservationNeeded =
XRE_IsParentProcess() &&
mozilla::GetEffectiveContentSandboxLevel() >= 3;
#else
const bool sIsStreamPreservationNeeded = false;
#endif // defined(MOZ_SANDBOX)
typename paramType::EnvType env;
mozilla::mscom::ProxyStreamFlags flags =
sIsStreamPreservationNeeded
? mozilla::mscom::ProxyStreamFlags::ePreservable
: mozilla::mscom::ProxyStreamFlags::eDefault;
mozilla::mscom::ProxyStream proxyStream(_IID, aParam.Get(), &env, flags);
int bufLen;
const BYTE* buf = proxyStream.GetBuffer(bufLen);
MOZ_ASSERT(buf || !bufLen);
aWriter->WriteInt(bufLen);
if (bufLen) {
aWriter->WriteBytes(reinterpret_cast<const char*>(buf), bufLen);
}
#if defined(MOZ_SANDBOX)
if (sIsStreamPreservationNeeded) {
/**
* When we're sending a ProxyStream from parent to content and the
* content sandboxing level is >= 3, content is unable to communicate
* its releasing of its reference to the proxied object. We preserve the
* marshaled proxy data here and later manually release it on content's
* behalf.
*/
aParam.PreserveStream(proxyStream.GetPreservedStream());
}
#endif // defined(MOZ_SANDBOX)
}
static bool Read(MessageReader* aReader, paramType* aResult) {
int length;
if (!aReader->ReadLength(&length)) {
return false;
}
mozilla::UniquePtr<BYTE[]> buf;
if (length) {
buf = mozilla::MakeUnique<BYTE[]>(length);
if (!aReader->ReadBytesInto(buf.get(), length)) {
return false;
}
}
typename paramType::EnvType env;
mozilla::mscom::ProxyStream proxyStream(_IID, buf.get(), length, &env);
if (!proxyStream.IsValid()) {
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::ProxyStreamValid, "false"_ns);
return false;
}
typename paramType::COMPtrType ptr;
if (!proxyStream.GetInterface(mozilla::mscom::getter_AddRefs(ptr))) {
return false;
}
aResult->Set(std::move(ptr));
return true;
}
};
} // namespace IPC
#endif // mozilla_mscom_COMPtrHolder_h

View File

@ -1,156 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/mscom/DispatchForwarder.h"
#include <oleauto.h>
#include <utility>
#include "mozilla/mscom/MainThreadInvoker.h"
namespace mozilla {
namespace mscom {
/* static */
HRESULT DispatchForwarder::Create(IInterceptor* aInterceptor,
STAUniquePtr<IDispatch>& aTarget,
IUnknown** aOutput) {
MOZ_ASSERT(aInterceptor && aOutput);
if (!aOutput) {
return E_INVALIDARG;
}
*aOutput = nullptr;
if (!aInterceptor) {
return E_INVALIDARG;
}
DispatchForwarder* forwarder = new DispatchForwarder(aInterceptor, aTarget);
HRESULT hr = forwarder->QueryInterface(IID_IDispatch, (void**)aOutput);
forwarder->Release();
return hr;
}
DispatchForwarder::DispatchForwarder(IInterceptor* aInterceptor,
STAUniquePtr<IDispatch>& aTarget)
: mRefCnt(1), mInterceptor(aInterceptor), mTarget(std::move(aTarget)) {}
DispatchForwarder::~DispatchForwarder() {}
HRESULT
DispatchForwarder::QueryInterface(REFIID riid, void** ppv) {
if (!ppv) {
return E_INVALIDARG;
}
// Since this class implements a tearoff, any interfaces that are not
// IDispatch must be routed to the original object's QueryInterface.
// This is especially important for IUnknown since COM uses that interface
// to determine object identity.
if (riid != IID_IDispatch) {
return mInterceptor->QueryInterface(riid, ppv);
}
IUnknown* punk = static_cast<IDispatch*>(this);
*ppv = punk;
if (!punk) {
return E_NOINTERFACE;
}
punk->AddRef();
return S_OK;
}
ULONG
DispatchForwarder::AddRef() {
return (ULONG)InterlockedIncrement((LONG*)&mRefCnt);
}
ULONG
DispatchForwarder::Release() {
ULONG newRefCnt = (ULONG)InterlockedDecrement((LONG*)&mRefCnt);
if (newRefCnt == 0) {
delete this;
}
return newRefCnt;
}
HRESULT
DispatchForwarder::GetTypeInfoCount(UINT* pctinfo) {
if (!pctinfo) {
return E_INVALIDARG;
}
*pctinfo = 1;
return S_OK;
}
HRESULT
DispatchForwarder::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) {
// ITypeInfo as implemented by COM is apartment-neutral, so we don't need
// to wrap it (yay!)
if (mTypeInfo) {
RefPtr<ITypeInfo> copy(mTypeInfo);
copy.forget(ppTInfo);
return S_OK;
}
HRESULT hr = E_UNEXPECTED;
auto fn = [&]() -> void { hr = mTarget->GetTypeInfo(iTInfo, lcid, ppTInfo); };
MainThreadInvoker invoker;
if (!invoker.Invoke(
NS_NewRunnableFunction("DispatchForwarder::GetTypeInfo", fn))) {
return E_UNEXPECTED;
}
if (FAILED(hr)) {
return hr;
}
mTypeInfo = *ppTInfo;
return hr;
}
HRESULT
DispatchForwarder::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
LCID lcid, DISPID* rgDispId) {
HRESULT hr = E_UNEXPECTED;
auto fn = [&]() -> void {
hr = mTarget->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
};
MainThreadInvoker invoker;
if (!invoker.Invoke(
NS_NewRunnableFunction("DispatchForwarder::GetIDsOfNames", fn))) {
return E_UNEXPECTED;
}
return hr;
}
HRESULT
DispatchForwarder::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
UINT* puArgErr) {
HRESULT hr;
if (!mInterface) {
if (!mTypeInfo) {
return E_UNEXPECTED;
}
TYPEATTR* typeAttr = nullptr;
hr = mTypeInfo->GetTypeAttr(&typeAttr);
if (FAILED(hr)) {
return hr;
}
hr = mInterceptor->QueryInterface(typeAttr->guid,
(void**)getter_AddRefs(mInterface));
mTypeInfo->ReleaseTypeAttr(typeAttr);
if (FAILED(hr)) {
return hr;
}
}
// We don't invoke IDispatch on the target, but rather on the interceptor!
hr = ::DispInvoke(mInterface.get(), mTypeInfo, dispIdMember, wFlags,
pDispParams, pVarResult, pExcepInfo, puArgErr);
return hr;
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,79 +0,0 @@
/* -*- 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

@ -1,164 +0,0 @@
/* -*- 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/FastMarshaler.h"
#include "mozilla/mscom/Utils.h"
#include <objbase.h>
namespace mozilla {
namespace mscom {
HRESULT
FastMarshaler::Create(IUnknown* aOuter, IUnknown** aOutMarshalerUnk) {
MOZ_ASSERT(XRE_IsContentProcess());
if (!aOuter || !aOutMarshalerUnk) {
return E_INVALIDARG;
}
*aOutMarshalerUnk = nullptr;
HRESULT hr;
RefPtr<FastMarshaler> fm(new FastMarshaler(aOuter, &hr));
if (FAILED(hr)) {
return hr;
}
return fm->InternalQueryInterface(IID_IUnknown, (void**)aOutMarshalerUnk);
}
FastMarshaler::FastMarshaler(IUnknown* aOuter, HRESULT* aResult)
: mRefCnt(0), mOuter(aOuter), mStdMarshalWeak(nullptr) {
*aResult =
::CoGetStdMarshalEx(aOuter, SMEXF_SERVER, getter_AddRefs(mStdMarshalUnk));
if (FAILED(*aResult)) {
return;
}
*aResult =
mStdMarshalUnk->QueryInterface(IID_IMarshal, (void**)&mStdMarshalWeak);
if (FAILED(*aResult)) {
return;
}
// mStdMarshalWeak is weak
mStdMarshalWeak->Release();
}
HRESULT
FastMarshaler::InternalQueryInterface(REFIID riid, void** ppv) {
if (!ppv) {
return E_INVALIDARG;
}
if (riid == IID_IUnknown) {
RefPtr<IUnknown> punk(static_cast<IUnknown*>(&mInternalUnknown));
punk.forget(ppv);
return S_OK;
}
if (riid == IID_IMarshal) {
RefPtr<IMarshal> ptr(this);
ptr.forget(ppv);
return S_OK;
}
return mStdMarshalUnk->QueryInterface(riid, ppv);
}
ULONG
FastMarshaler::InternalAddRef() { return ++mRefCnt; }
ULONG
FastMarshaler::InternalRelease() {
ULONG result = --mRefCnt;
if (!result) {
delete this;
}
return result;
}
DWORD
FastMarshaler::GetMarshalFlags(DWORD aDestContext, DWORD aMshlFlags) {
// Only worry about local contexts.
if (aDestContext != MSHCTX_LOCAL) {
return aMshlFlags;
}
return aMshlFlags | MSHLFLAGS_NOPING;
}
HRESULT
FastMarshaler::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) {
if (!mStdMarshalWeak) {
return E_POINTER;
}
return mStdMarshalWeak->GetUnmarshalClass(
riid, pv, dwDestContext, pvDestContext,
GetMarshalFlags(dwDestContext, mshlflags), pCid);
}
HRESULT
FastMarshaler::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) {
if (!mStdMarshalWeak) {
return E_POINTER;
}
return mStdMarshalWeak->GetMarshalSizeMax(
riid, pv, dwDestContext, pvDestContext,
GetMarshalFlags(dwDestContext, mshlflags), pSize);
}
HRESULT
FastMarshaler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) {
if (!mStdMarshalWeak) {
return E_POINTER;
}
return mStdMarshalWeak->MarshalInterface(
pStm, riid, pv, dwDestContext, pvDestContext,
GetMarshalFlags(dwDestContext, mshlflags));
}
HRESULT
FastMarshaler::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
if (!mStdMarshalWeak) {
return E_POINTER;
}
return mStdMarshalWeak->UnmarshalInterface(pStm, riid, ppv);
}
HRESULT
FastMarshaler::ReleaseMarshalData(IStream* pStm) {
if (!mStdMarshalWeak) {
return E_POINTER;
}
return mStdMarshalWeak->ReleaseMarshalData(pStm);
}
HRESULT
FastMarshaler::DisconnectObject(DWORD dwReserved) {
if (!mStdMarshalWeak) {
return E_POINTER;
}
return mStdMarshalWeak->DisconnectObject(dwReserved);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,66 +0,0 @@
/* -*- 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_FastMarshaler_h
#define mozilla_mscom_FastMarshaler_h
#include "mozilla/Atomics.h"
#include "mozilla/mscom/Aggregation.h"
#include "mozilla/RefPtr.h"
#include <objidl.h>
namespace mozilla {
namespace mscom {
/**
* COM ping functionality is enabled by default and is designed to free strong
* references held by defunct client processes. However, this incurs a
* significant performance penalty in a11y code due to large numbers of remote
* objects being created and destroyed within a short period of time. Thus, we
* turn off pings to improve performance.
* ACHTUNG! When COM pings are disabled, Release calls from remote clients are
* never sent to the server! If you use this marshaler, you *must* explicitly
* disconnect clients using CoDisconnectObject when the object is no longer
* relevant. Otherwise, references to the object will never be released, causing
* a leak.
*/
class FastMarshaler final : public IMarshal {
public:
static HRESULT Create(IUnknown* aOuter, IUnknown** aOutMarshalerUnk);
// IMarshal
STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) override;
STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) override;
STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) override;
STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid,
void** ppv) override;
STDMETHODIMP ReleaseMarshalData(IStream* pStm) override;
STDMETHODIMP DisconnectObject(DWORD dwReserved) override;
private:
FastMarshaler(IUnknown* aOuter, HRESULT* aResult);
~FastMarshaler() = default;
static DWORD GetMarshalFlags(DWORD aDestContext, DWORD aMshlFlags);
Atomic<ULONG> mRefCnt;
IUnknown* mOuter;
RefPtr<IUnknown> mStdMarshalUnk;
IMarshal* mStdMarshalWeak;
DECLARE_AGGREGATABLE(FastMarshaler);
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_FastMarshaler_h

View File

@ -1,51 +0,0 @@
/* -*- 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_IHandlerProvider_h
#define mozilla_mscom_IHandlerProvider_h
#include "mozilla/NotNull.h"
#include "mozilla/mscom/Ptr.h"
#include <objidl.h>
namespace mozilla {
namespace mscom {
struct IInterceptor;
struct HandlerProvider {
virtual STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) = 0;
virtual STDMETHODIMP GetHandlerPayloadSize(
NotNull<IInterceptor*> aInterceptor, NotNull<DWORD*> aOutPayloadSize) = 0;
virtual STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
NotNull<IStream*> aStream) = 0;
virtual STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) = 0;
virtual STDMETHODIMP DisconnectHandlerRemotes() = 0;
/**
* Determine whether this interface might be supported by objects using
* this HandlerProvider.
* This is used to avoid unnecessary cross-thread QueryInterface calls for
* interfaces known to be unsupported.
* Return S_OK if the interface might be supported, E_NOINTERFACE if it
* definitely isn't supported.
*/
virtual STDMETHODIMP IsInterfaceMaybeSupported(REFIID aIid) { return S_OK; }
};
struct IHandlerProvider : public IUnknown, public HandlerProvider {
virtual STDMETHODIMP_(REFIID)
GetEffectiveOutParamIid(REFIID aCallIid, ULONG aCallMethod) = 0;
virtual STDMETHODIMP NewInstance(
REFIID aIid, InterceptorTargetPtr<IUnknown> aTarget,
NotNull<IHandlerProvider**> aOutNewPayload) = 0;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_IHandlerProvider_h

View File

@ -1,860 +0,0 @@
/* -*- 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 <utility>
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/Unused.h"
#include "mozilla/mscom/DispatchForwarder.h"
#include "mozilla/mscom/FastMarshaler.h"
#include "mozilla/mscom/InterceptorLog.h"
#include "mozilla/mscom/MainThreadInvoker.h"
#include "mozilla/mscom/Objref.h"
#include "mozilla/mscom/Registration.h"
#include "mozilla/mscom/Utils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsPrintfCString.h"
#include "nsRefPtrHashtable.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#define ENSURE_HR_SUCCEEDED(hr) \
MOZ_ASSERT(SUCCEEDED((HRESULT)hr)); \
if (FAILED((HRESULT)hr)) { \
return hr; \
}
namespace mozilla {
namespace mscom {
namespace detail {
class MOZ_CAPABILITY("mutex") LiveSet final {
public:
LiveSet() : mMutex("mozilla::mscom::LiveSet::mMutex") {}
void Lock() MOZ_CAPABILITY_ACQUIRE(mMutex) { mMutex.Lock(); }
void Unlock() MOZ_CAPABILITY_RELEASE(mMutex) { mMutex.Unlock(); }
void Put(IUnknown* aKey, already_AddRefed<IWeakReference> aValue) {
mMutex.AssertCurrentThreadOwns();
mLiveSet.InsertOrUpdate(aKey, RefPtr<IWeakReference>{std::move(aValue)});
}
RefPtr<IWeakReference> Get(IUnknown* aKey) {
mMutex.AssertCurrentThreadOwns();
RefPtr<IWeakReference> result;
mLiveSet.Get(aKey, getter_AddRefs(result));
return result;
}
void Remove(IUnknown* aKey) {
mMutex.AssertCurrentThreadOwns();
mLiveSet.Remove(aKey);
}
private:
Mutex mMutex MOZ_UNANNOTATED;
nsRefPtrHashtable<nsPtrHashKey<IUnknown>, IWeakReference> mLiveSet;
};
/**
* We don't use the normal XPCOM BaseAutoLock because we need the ability
* to explicitly Unlock.
*/
class MOZ_RAII MOZ_SCOPED_CAPABILITY LiveSetAutoLock final {
public:
explicit LiveSetAutoLock(LiveSet& aLiveSet) MOZ_CAPABILITY_ACQUIRE(aLiveSet)
: mLiveSet(&aLiveSet) {
aLiveSet.Lock();
}
~LiveSetAutoLock() MOZ_CAPABILITY_RELEASE() {
if (mLiveSet) {
mLiveSet->Unlock();
}
}
void Unlock() MOZ_CAPABILITY_RELEASE() {
MOZ_ASSERT(mLiveSet);
if (mLiveSet) {
mLiveSet->Unlock();
mLiveSet = nullptr;
}
}
LiveSetAutoLock(const LiveSetAutoLock& aOther) = delete;
LiveSetAutoLock(LiveSetAutoLock&& aOther) = delete;
LiveSetAutoLock& operator=(const LiveSetAutoLock& aOther) = delete;
LiveSetAutoLock& operator=(LiveSetAutoLock&& aOther) = delete;
private:
LiveSet* mLiveSet;
};
class MOZ_RAII ReentrySentinel final {
public:
explicit ReentrySentinel(Interceptor* aCurrent) : mCurInterceptor(aCurrent) {
static const bool kHasTls = tlsSentinelStackTop.init();
MOZ_RELEASE_ASSERT(kHasTls);
mPrevSentinel = tlsSentinelStackTop.get();
tlsSentinelStackTop.set(this);
}
~ReentrySentinel() { tlsSentinelStackTop.set(mPrevSentinel); }
bool IsOutermost() const {
return !(mPrevSentinel && mPrevSentinel->IsMarshaling(mCurInterceptor));
}
ReentrySentinel(const ReentrySentinel&) = delete;
ReentrySentinel(ReentrySentinel&&) = delete;
ReentrySentinel& operator=(const ReentrySentinel&) = delete;
ReentrySentinel& operator=(ReentrySentinel&&) = delete;
private:
bool IsMarshaling(Interceptor* aTopInterceptor) const {
return aTopInterceptor == mCurInterceptor ||
(mPrevSentinel && mPrevSentinel->IsMarshaling(aTopInterceptor));
}
private:
Interceptor* mCurInterceptor;
ReentrySentinel* mPrevSentinel;
static MOZ_THREAD_LOCAL(ReentrySentinel*) tlsSentinelStackTop;
};
MOZ_THREAD_LOCAL(ReentrySentinel*) ReentrySentinel::tlsSentinelStackTop;
class MOZ_RAII LoggedQIResult final {
public:
explicit LoggedQIResult(REFIID aIid)
: mIid(aIid),
mHr(E_UNEXPECTED),
mTarget(nullptr),
mInterceptor(nullptr),
mBegin(TimeStamp::Now()) {}
~LoggedQIResult() {
if (!mTarget) {
return;
}
TimeStamp end(TimeStamp::Now());
TimeDuration total(end - mBegin);
TimeDuration overhead(total - mNonOverheadDuration);
InterceptorLog::QI(mHr, mTarget, mIid, mInterceptor, &overhead,
&mNonOverheadDuration);
}
void Log(IUnknown* aTarget, IUnknown* aInterceptor) {
mTarget = aTarget;
mInterceptor = aInterceptor;
}
void operator=(HRESULT aHr) { mHr = aHr; }
operator HRESULT() { return mHr; }
operator TimeDuration*() { return &mNonOverheadDuration; }
LoggedQIResult(const LoggedQIResult&) = delete;
LoggedQIResult(LoggedQIResult&&) = delete;
LoggedQIResult& operator=(const LoggedQIResult&) = delete;
LoggedQIResult& operator=(LoggedQIResult&&) = delete;
private:
REFIID mIid;
HRESULT mHr;
IUnknown* mTarget;
IUnknown* mInterceptor;
TimeDuration mNonOverheadDuration;
TimeStamp mBegin;
};
} // namespace detail
static detail::LiveSet& GetLiveSet() {
static detail::LiveSet sLiveSet;
return sLiveSet;
}
MOZ_THREAD_LOCAL(bool) Interceptor::tlsCreatingStdMarshal;
/* static */
HRESULT Interceptor::Create(STAUniquePtr<IUnknown> aTarget,
IInterceptorSink* aSink, REFIID aInitialIid,
void** aOutInterface) {
MOZ_ASSERT(aOutInterface && aTarget && aSink);
if (!aOutInterface) {
return E_INVALIDARG;
}
detail::LiveSetAutoLock lock(GetLiveSet());
RefPtr<IWeakReference> existingWeak(GetLiveSet().Get(aTarget.get()));
if (existingWeak) {
RefPtr<IWeakReferenceSource> existingStrong;
if (SUCCEEDED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
// QI on existingStrong may touch other threads. Since we now hold a
// strong ref on the interceptor, we may now release the lock.
lock.Unlock();
return existingStrong->QueryInterface(aInitialIid, aOutInterface);
}
}
*aOutInterface = nullptr;
if (!aTarget || !aSink) {
return E_INVALIDARG;
}
RefPtr<Interceptor> intcpt(new Interceptor(aSink));
return intcpt->GetInitialInterceptorForIID(lock, aInitialIid,
std::move(aTarget), aOutInterface);
}
Interceptor::Interceptor(IInterceptorSink* aSink)
: WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread),
mEventSink(aSink),
mInterceptorMapMutex("mozilla::mscom::Interceptor::mInterceptorMapMutex"),
mStdMarshalMutex("mozilla::mscom::Interceptor::mStdMarshalMutex"),
mStdMarshal(nullptr) {
static const bool kHasTls = tlsCreatingStdMarshal.init();
MOZ_ASSERT(kHasTls);
Unused << kHasTls;
MOZ_ASSERT(aSink);
RefPtr<IWeakReference> weakRef;
if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
aSink->SetInterceptor(weakRef);
}
}
Interceptor::~Interceptor() {
{ // Scope for lock
detail::LiveSetAutoLock lock(GetLiveSet());
GetLiveSet().Remove(mTarget.get());
}
// 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 = nullptr;
entry.mTargetInterface->Release();
}
}
HRESULT
Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
CLSID* aHandlerClsid) {
if (aDestContextPtr || !aHandlerClsid ||
aDestContext == MSHCTX_DIFFERENTMACHINE) {
return E_INVALIDARG;
}
MOZ_ASSERT(mEventSink);
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
}
REFIID
Interceptor::MarshalAs(REFIID aIid) const {
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
return IsCallerExternalProcess() ? aIid : mEventSink->MarshalAs(aIid);
#else
return mEventSink->MarshalAs(aIid);
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
}
HRESULT
Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) {
return mStdMarshal->GetUnmarshalClass(MarshalAs(riid), pv, dwDestContext,
pvDestContext, mshlflags, pCid);
}
HRESULT
Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) {
detail::ReentrySentinel sentinel(this);
HRESULT hr = mStdMarshal->GetMarshalSizeMax(
MarshalAs(riid), pv, dwDestContext, pvDestContext, mshlflags, pSize);
if (FAILED(hr) || !sentinel.IsOutermost()) {
return hr;
}
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
if (XRE_IsContentProcess() && IsCallerExternalProcess()) {
// The caller isn't our chrome process, so we do not provide a handler
// payload. Even though we're only getting the size here, calculating the
// payload size might actually require building the payload.
return hr;
}
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
DWORD payloadSize = 0;
hr = mEventSink->GetHandlerPayloadSize(WrapNotNull(this),
WrapNotNull(&payloadSize));
if (hr == E_NOTIMPL) {
return S_OK;
}
if (SUCCEEDED(hr)) {
*pSize += payloadSize;
}
return hr;
}
HRESULT
Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) {
detail::ReentrySentinel sentinel(this);
HRESULT hr;
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
// Save the current stream position
LARGE_INTEGER seekTo;
seekTo.QuadPart = 0;
ULARGE_INTEGER objrefPos;
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
if (FAILED(hr)) {
return hr;
}
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
hr = mStdMarshal->MarshalInterface(pStm, MarshalAs(riid), pv, dwDestContext,
pvDestContext, mshlflags);
if (FAILED(hr) || !sentinel.IsOutermost()) {
return hr;
}
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
if (XRE_IsContentProcess() && IsCallerExternalProcess()) {
// The caller isn't our chrome process, so do not provide a handler.
// First, save the current position that marks the current end of the
// OBJREF in the stream.
ULARGE_INTEGER endPos;
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &endPos);
if (FAILED(hr)) {
return hr;
}
// Now strip out the handler.
if (!StripHandlerFromOBJREF(WrapNotNull(pStm), objrefPos.QuadPart,
endPos.QuadPart)) {
return E_FAIL;
}
return S_OK;
}
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
hr = mEventSink->WriteHandlerPayload(WrapNotNull(this), WrapNotNull(pStm));
if (hr == E_NOTIMPL) {
return S_OK;
}
return hr;
}
HRESULT
Interceptor::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
return mStdMarshal->UnmarshalInterface(pStm, riid, ppv);
}
HRESULT
Interceptor::ReleaseMarshalData(IStream* pStm) {
return mStdMarshal->ReleaseMarshalData(pStm);
}
HRESULT
Interceptor::DisconnectObject(DWORD dwReserved) {
mEventSink->DisconnectHandlerRemotes();
return mStdMarshal->DisconnectObject(dwReserved);
}
Interceptor::MapEntry* Interceptor::Lookup(REFIID aIid) {
mInterceptorMapMutex.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<IUnknown>& aTarget) {
MutexAutoLock lock(mInterceptorMapMutex);
MapEntry* entry = Lookup(aIid);
if (entry) {
aTarget.reset(entry->mTargetInterface);
return S_OK;
}
return E_NOINTERFACE;
}
// CoGetInterceptor requires type metadata to be able to generate its emulated
// vtable. If no registered metadata is available, CoGetInterceptor returns
// kFileNotFound.
static const HRESULT kFileNotFound = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
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;
}
HRESULT
Interceptor::PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
RefPtr<IUnknown> aInterceptor, REFIID aTargetIid,
STAUniquePtr<IUnknown> aTarget)
MOZ_NO_THREAD_SAFETY_ANALYSIS {
// Suppress thread safety analysis as this conditionally releases locks.
RefPtr<IWeakReference> weakRef;
HRESULT hr = GetWeakReference(getter_AddRefs(weakRef));
if (FAILED(hr)) {
return hr;
}
// mTarget is a weak reference to aTarget. This is safe because we transfer
// ownership of aTarget into mInterceptorMap which remains live for the
// lifetime of this Interceptor.
mTarget = ToInterceptorTargetPtr(aTarget);
GetLiveSet().Put(mTarget.get(), weakRef.forget());
// Now we transfer aTarget's ownership into mInterceptorMap.
mInterceptorMap.AppendElement(
MapEntry(aTargetIid, aInterceptor, aTarget.release()));
// Release the live set lock because subsequent operations may post work to
// the main thread, creating potential for deadlocks.
aLiveSetLock.Unlock();
return S_OK;
}
HRESULT
Interceptor::GetInitialInterceptorForIID(
detail::LiveSetAutoLock& aLiveSetLock, REFIID aTargetIid,
STAUniquePtr<IUnknown> aTarget,
void** aOutInterceptor) MOZ_NO_THREAD_SAFETY_ANALYSIS {
// Suppress thread safety analysis as this conditionally releases locks.
MOZ_ASSERT(aOutInterceptor);
MOZ_ASSERT(aTargetIid != IID_IMarshal);
MOZ_ASSERT(!IsProxy(aTarget.get()));
HRESULT hr = E_UNEXPECTED;
auto hasFailed = [&hr]() -> bool { return FAILED(hr); };
MOZ_PUSH_IGNORE_THREAD_SAFETY // Avoid the lambda upsetting analysis.
auto cleanup = [&aLiveSetLock]() -> void { aLiveSetLock.Unlock(); };
MOZ_POP_THREAD_SAFETY
ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed,
cleanup);
if (aTargetIid == IID_IUnknown) {
// We must lock mInterceptorMapMutex so that nothing can race with us once
// we have been published to the live set.
MutexAutoLock lock(mInterceptorMapMutex);
hr = PublishTarget(aLiveSetLock, nullptr, aTargetIid, std::move(aTarget));
ENSURE_HR_SUCCEEDED(hr);
hr = QueryInterface(aTargetIid, aOutInterceptor);
ENSURE_HR_SUCCEEDED(hr);
return hr;
}
// Raise the refcount for stabilization purposes during aggregation
WeakReferenceSupport::StabilizeRefCount stabilizer(*this);
RefPtr<IUnknown> unkInterceptor;
hr = CreateInterceptor(aTargetIid, static_cast<WeakReferenceSupport*>(this),
getter_AddRefs(unkInterceptor));
ENSURE_HR_SUCCEEDED(hr);
RefPtr<ICallInterceptor> interceptor;
hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
getter_AddRefs(interceptor));
ENSURE_HR_SUCCEEDED(hr);
hr = interceptor->RegisterSink(mEventSink);
ENSURE_HR_SUCCEEDED(hr);
// We must lock mInterceptorMapMutex so that nothing can race with us once we
// have been published to the live set.
MutexAutoLock lock(mInterceptorMapMutex);
hr = PublishTarget(aLiveSetLock, unkInterceptor, aTargetIid,
std::move(aTarget));
ENSURE_HR_SUCCEEDED(hr);
if (MarshalAs(aTargetIid) == aTargetIid) {
hr = unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
ENSURE_HR_SUCCEEDED(hr);
return hr;
}
hr = GetInterceptorForIID(aTargetIid, aOutInterceptor, &lock);
ENSURE_HR_SUCCEEDED(hr);
return hr;
}
HRESULT
Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor) {
return GetInterceptorForIID(aIid, aOutInterceptor, nullptr);
}
/**
* 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.
* @param aAlreadyLocked Proof of an existing lock on |mInterceptorMapMutex|,
* if present.
*/
HRESULT
Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor,
MutexAutoLock* aAlreadyLocked) {
detail::LoggedQIResult result(aIid);
if (!aOutInterceptor) {
return E_INVALIDARG;
}
if (aIid == IID_IUnknown) {
// Special case: When we see IUnknown, we just provide a reference to this
RefPtr<IInterceptor> intcpt(this);
intcpt.forget(aOutInterceptor);
return S_OK;
}
REFIID interceptorIid = MarshalAs(aIid);
RefPtr<IUnknown> unkInterceptor;
IUnknown* interfaceForQILog = nullptr;
// (1) Check to see if we already have an existing interceptor for
// interceptorIid.
auto doLookup = [&]() -> void {
MapEntry* entry = Lookup(interceptorIid);
if (entry) {
unkInterceptor = entry->mInterceptor;
interfaceForQILog = entry->mTargetInterface;
}
};
if (aAlreadyLocked) {
doLookup();
} else {
MutexAutoLock lock(mInterceptorMapMutex);
doLookup();
}
// (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.
result.Log(mTarget.get(), interfaceForQILog);
result = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
ENSURE_HR_SUCCEEDED(result);
return result;
}
// (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(interceptorIid, (void**)&rawTargetInterface, result);
targetInterface.reset(rawTargetInterface);
result = hr;
result.Log(mTarget.get(), targetInterface.get());
MOZ_ASSERT(SUCCEEDED(hr) || hr == E_NOINTERFACE);
if (hr == E_NOINTERFACE) {
return hr;
}
ENSURE_HR_SUCCEEDED(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
WeakReferenceSupport::StabilizeRefCount stabilizer(*this);
hr = CreateInterceptor(interceptorIid,
static_cast<WeakReferenceSupport*>(this),
getter_AddRefs(unkInterceptor));
ENSURE_HR_SUCCEEDED(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));
ENSURE_HR_SUCCEEDED(hr);
hr = interceptor->RegisterSink(mEventSink);
ENSURE_HR_SUCCEEDED(hr);
// (5) Now that we have this new COM interceptor, insert it into the map.
auto doInsertion = [&]() -> void {
// We might have raced with another thread, so first check that we don't
// already have an entry for this
MapEntry* entry = Lookup(interceptorIid);
if (entry && entry->mInterceptor) {
// Bug 1433046: Because of aggregation, the QI for |interceptor|
// AddRefed |this|, not |unkInterceptor|. Thus, releasing |unkInterceptor|
// will destroy the object. Before we do that, we must first release
// |interceptor|. Otherwise, |interceptor| would be invalidated when
// |unkInterceptor| is destroyed.
interceptor = nullptr;
unkInterceptor = entry->mInterceptor;
} else {
// MapEntry has a RefPtr to unkInterceptor, 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(interceptorIid, unkInterceptor, rawTargetInterface));
}
};
if (aAlreadyLocked) {
doInsertion();
} else {
MutexAutoLock lock(mInterceptorMapMutex);
doInsertion();
}
hr = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
ENSURE_HR_SUCCEEDED(hr);
return hr;
}
HRESULT
Interceptor::QueryInterfaceTarget(REFIID aIid, void** aOutput,
TimeDuration* aOutDuration) {
// 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*.
if (!NS_IsMainThread() && tlsCreatingStdMarshal.get()) {
mStdMarshalMutex.AssertCurrentThreadOwns();
// COM queries for special interfaces such as IFastRundown when creating a
// marshaler. We don't want these being dispatched to the main thread,
// since this would cause a deadlock on mStdMarshalMutex if the main
// thread is also querying for IMarshal. If we do need to respond to these
// special interfaces, this should be done before this point; e.g. in
// Interceptor::QueryInterface like we do for INoMarshal.
return E_NOINTERFACE;
}
if (mEventSink->IsInterfaceMaybeSupported(aIid) == E_NOINTERFACE) {
return E_NOINTERFACE;
}
MainThreadInvoker invoker;
HRESULT hr;
auto runOnMainThread = [&]() -> void {
MOZ_ASSERT(NS_IsMainThread());
hr = mTarget->QueryInterface(aIid, aOutput);
};
if (!invoker.Invoke(NS_NewRunnableFunction("Interceptor::QueryInterface",
runOnMainThread))) {
return E_FAIL;
}
if (aOutDuration) {
*aOutDuration = invoker.GetDuration();
}
return hr;
}
HRESULT
Interceptor::QueryInterface(REFIID riid, void** ppv) {
if (riid == IID_INoMarshal) {
// This entire library is designed around marshaling, so there's no point
// propagating this QI request all over the place!
return E_NOINTERFACE;
}
return WeakReferenceSupport::QueryInterface(riid, ppv);
}
HRESULT
Interceptor::WeakRefQueryInterface(REFIID aIid, IUnknown** aOutInterface) {
if (aIid == IID_IStdMarshalInfo) {
detail::ReentrySentinel sentinel(this);
if (!sentinel.IsOutermost()) {
return E_NOINTERFACE;
}
// Do not indicate that this interface is available unless we actually
// support it. We'll check that by looking for a successful call to
// IInterceptorSink::GetHandler()
CLSID dummy;
if (FAILED(mEventSink->GetHandler(WrapNotNull(&dummy)))) {
return E_NOINTERFACE;
}
RefPtr<IStdMarshalInfo> std(this);
std.forget(aOutInterface);
return S_OK;
}
if (aIid == IID_IMarshal) {
MutexAutoLock lock(mStdMarshalMutex);
HRESULT hr;
if (!mStdMarshalUnk) {
MOZ_ASSERT(!tlsCreatingStdMarshal.get());
tlsCreatingStdMarshal.set(true);
if (XRE_IsContentProcess()) {
hr = FastMarshaler::Create(static_cast<IWeakReferenceSource*>(this),
getter_AddRefs(mStdMarshalUnk));
} else {
hr = ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource*>(this),
SMEXF_SERVER, getter_AddRefs(mStdMarshalUnk));
}
tlsCreatingStdMarshal.set(false);
ENSURE_HR_SUCCEEDED(hr);
}
if (!mStdMarshal) {
hr = mStdMarshalUnk->QueryInterface(IID_IMarshal, (void**)&mStdMarshal);
ENSURE_HR_SUCCEEDED(hr);
// mStdMarshal is weak, so drop its refcount
mStdMarshal->Release();
}
RefPtr<IMarshal> marshal(this);
marshal.forget(aOutInterface);
return S_OK;
}
if (aIid == IID_IInterceptor) {
RefPtr<IInterceptor> intcpt(this);
intcpt.forget(aOutInterface);
return S_OK;
}
if (aIid == IID_IDispatch) {
STAUniquePtr<IDispatch> disp;
IDispatch* rawDisp = nullptr;
HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
ENSURE_HR_SUCCEEDED(hr);
disp.reset(rawDisp);
return DispatchForwarder::Create(this, disp, aOutInterface);
}
return GetInterceptorForIID(aIid, (void**)aOutInterface, nullptr);
}
ULONG
Interceptor::AddRef() { return WeakReferenceSupport::AddRef(); }
ULONG
Interceptor::Release() { return WeakReferenceSupport::Release(); }
/* static */
HRESULT Interceptor::DisconnectRemotesForTarget(IUnknown* aTarget) {
MOZ_ASSERT(aTarget);
detail::LiveSetAutoLock lock(GetLiveSet());
// It is not an error if the interceptor doesn't exist, so we return
// S_FALSE instead of an error in that case.
RefPtr<IWeakReference> existingWeak(GetLiveSet().Get(aTarget));
if (!existingWeak) {
return S_FALSE;
}
RefPtr<IWeakReferenceSource> existingStrong;
if (FAILED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
return S_FALSE;
}
// Since we now hold a strong ref on the interceptor, we may now release the
// lock.
lock.Unlock();
return ::CoDisconnectObject(existingStrong, 0);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,199 +0,0 @@
/* -*- 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 <callobj.h>
#include <objidl.h>
#include <utility>
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/mscom/IHandlerProvider.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/mscom/WeakRef.h"
#include "nsTArray.h"
namespace mozilla {
namespace mscom {
namespace detail {
class LiveSetAutoLock;
} // namespace detail
// {8831EB53-A937-42BC-9921-B3E1121FDF86}
DEFINE_GUID(IID_IInterceptorSink, 0x8831eb53, 0xa937, 0x42bc, 0x99, 0x21, 0xb3,
0xe1, 0x12, 0x1f, 0xdf, 0x86);
struct IInterceptorSink : public ICallFrameEvents, public HandlerProvider {
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<IUnknown>& aTarget) = 0;
virtual STDMETHODIMP GetInterceptorForIID(REFIID aIid,
void** aOutInterceptor) = 0;
virtual STDMETHODIMP GetEventSink(IInterceptorSink** aSink) = 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).
*
* ACHTUNG! mscom::Interceptor uses FastMarshaler to disable COM garbage
* collection. If you use this class, you *must* call
* Interceptor::DisconnectRemotesForTarget when an object is no longer relevant.
* Otherwise, the object will never be released, causing a leak.
*/
class Interceptor final : public WeakReferenceSupport,
public IStdMarshalInfo,
public IMarshal,
public IInterceptor {
public:
static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
REFIID aInitialIid, void** aOutInterface);
/**
* Disconnect all remote clients for a given target.
* Because Interceptors disable COM garbage collection to improve
* performance, they never receive Release calls from remote clients. If
* the object can be shut down while clients still hold a reference, this
* function can be used to force COM to disconnect all remote connections
* (using CoDisconnectObject) and thus release the associated references to
* the Interceptor, its target and any objects associated with the
* HandlerProvider.
* Note that the specified target must be the same IUnknown pointer used to
* create the Interceptor. Where there is multiple inheritance, querying for
* IID_IUnknown and calling this function with that pointer alone will not
* disconnect remotes for all interfaces. If you expect that the same object
* may be fetched with different initial interfaces, you should call this
* function once for each possible IUnknown pointer.
* @return S_OK if there was an Interceptor for the given target,
* S_FALSE if there was not.
*/
static HRESULT DisconnectRemotesForTarget(IUnknown* aTarget);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IStdMarshalInfo
STDMETHODIMP GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
CLSID* aHandlerClsid) override;
// IMarshal
STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) override;
STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) override;
STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) override;
STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid,
void** ppv) override;
STDMETHODIMP ReleaseMarshalData(IStream* pStm) override;
STDMETHODIMP DisconnectObject(DWORD dwReserved) override;
// IInterceptor
STDMETHODIMP GetTargetForIID(
REFIID aIid, InterceptorTargetPtr<IUnknown>& aTarget) override;
STDMETHODIMP GetInterceptorForIID(REFIID aIid,
void** aOutInterceptor) override;
STDMETHODIMP GetEventSink(IInterceptorSink** aSink) override {
RefPtr<IInterceptorSink> sink = mEventSink;
sink.forget(aSink);
return mEventSink ? S_OK : S_FALSE;
}
private:
struct MapEntry {
MapEntry(REFIID aIid, IUnknown* aInterceptor, IUnknown* aTargetInterface)
: mIID(aIid),
mInterceptor(aInterceptor),
mTargetInterface(aTargetInterface) {}
IID mIID;
RefPtr<IUnknown> mInterceptor;
IUnknown* mTargetInterface;
};
private:
explicit Interceptor(IInterceptorSink* aSink);
~Interceptor();
HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
REFIID aTargetIid,
STAUniquePtr<IUnknown> aTarget,
void** aOutInterface);
HRESULT GetInterceptorForIID(REFIID aIid, void** aOutInterceptor,
MutexAutoLock* aAlreadyLocked);
MapEntry* Lookup(REFIID aIid);
HRESULT QueryInterfaceTarget(REFIID aIid, void** aOutput,
TimeDuration* aOutDuration = nullptr);
HRESULT WeakRefQueryInterface(REFIID aIid, IUnknown** aOutInterface) override;
HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput);
REFIID MarshalAs(REFIID aIid) const;
HRESULT PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
RefPtr<IUnknown> aInterceptor, REFIID aTargetIid,
STAUniquePtr<IUnknown> aTarget);
private:
InterceptorTargetPtr<IUnknown> mTarget;
RefPtr<IInterceptorSink> mEventSink;
mozilla::Mutex mInterceptorMapMutex
MOZ_UNANNOTATED; // Guards mInterceptorMap
// Using a nsTArray since the # of interfaces is not going to be very high
nsTArray<MapEntry> mInterceptorMap;
mozilla::Mutex mStdMarshalMutex
MOZ_UNANNOTATED; // Guards mStdMarshalUnk and mStdMarshal
RefPtr<IUnknown> mStdMarshalUnk;
IMarshal* mStdMarshal; // WEAK
static MOZ_THREAD_LOCAL(bool) tlsCreatingStdMarshal;
};
template <typename InterfaceT>
inline HRESULT CreateInterceptor(STAUniquePtr<InterfaceT> aTargetInterface,
IInterceptorSink* aEventSink,
InterfaceT** aOutInterface) {
if (!aTargetInterface || !aEventSink) {
return E_INVALIDARG;
}
REFIID iidTarget = __uuidof(InterfaceT);
STAUniquePtr<IUnknown> targetUnknown(aTargetInterface.release());
return Interceptor::Create(std::move(targetUnknown), aEventSink, iidTarget,
(void**)aOutInterface);
}
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Interceptor_h

View File

@ -1,527 +0,0 @@
/* -*- 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 <callobj.h>
#include <utility>
#include "MainThreadUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/Mutex.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "mozilla/mscom/Registration.h"
#include "mozilla/mscom/Utils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIOutputStream.h"
#include "nsIThread.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsReadableUtils.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsXPCOMPrivate.h"
#include "nsXULAppAPI.h"
#include "prenv.h"
#include "prio.h"
using mozilla::DebugOnly;
using mozilla::Mutex;
using mozilla::MutexAutoLock;
using mozilla::NewNonOwningRunnableMethod;
using mozilla::StaticAutoPtr;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
using mozilla::Unused;
using mozilla::mscom::ArrayData;
using mozilla::mscom::FindArrayData;
using mozilla::mscom::IsValidGUID;
using mozilla::services::GetObserverService;
namespace {
class ShutdownEvent final : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
private:
~ShutdownEvent() {}
};
NS_IMPL_ISUPPORTS(ShutdownEvent, nsIObserver)
class Logger final {
public:
explicit Logger(const nsACString& aLeafBaseName);
bool IsValid() {
MutexAutoLock lock(mMutex);
return !!mThread;
}
void LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid,
IUnknown* aInterface, const TimeDuration* aOverheadDuration,
const TimeDuration* aGeckoDuration);
void CaptureFrame(ICallFrame* aCallFrame, IUnknown* aTargetInterface,
nsACString& aCapturedFrame);
void LogEvent(const nsACString& aCapturedFrame,
const TimeDuration& aOverheadDuration,
const TimeDuration& aGeckoDuration);
nsresult Shutdown();
private:
void OpenFile();
void Flush();
void CloseFile();
void AssertRunningOnLoggerThread();
bool VariantToString(const VARIANT& aVariant, nsACString& aOut,
LONG aIndex = 0);
bool TryParamAsGuid(REFIID aIid, ICallFrame* aCallFrame,
const CALLFRAMEPARAMINFO& aParamInfo, nsACString& aLine);
static double GetElapsedTime();
nsCOMPtr<nsIFile> mLogFileName;
nsCOMPtr<nsIOutputStream> mLogFile; // Only accessed by mThread
Mutex mMutex MOZ_UNANNOTATED; // 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;
nsresult rv =
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(logFileName));
if (NS_FAILED(rv)) {
return;
}
GeckoProcessType procType = XRE_GetProcessType();
nsAutoCString leafName(aLeafBaseName);
if (procType == GeckoProcessType_Default) {
leafName.AppendLiteral("-Parent-");
} else if (procType == GeckoProcessType_Content) {
// Note that this logging won't work in opt build content processes unless
// the sandbox is disabled. It should still work in debug builds because we
// give access to the os temp dir.
leafName.AppendLiteral("-Content-");
} else {
return;
}
DWORD pid = GetCurrentProcessId();
leafName.AppendPrintf("%lu.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("Logger::OpenFile", 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("Logger::CloseFile", 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");
(void)rv;
return NS_OK;
}
bool Logger::VariantToString(const VARIANT& aVariant, nsACString& aOut,
LONG aIndex) {
switch (aVariant.vt) {
case VT_DISPATCH: {
aOut.AppendPrintf("(IDispatch*) 0x%p", aVariant.pdispVal);
return true;
}
case VT_DISPATCH | VT_BYREF: {
aOut.AppendPrintf("(IDispatch*) 0x%p", (aVariant.ppdispVal)[aIndex]);
return true;
}
case VT_UNKNOWN: {
aOut.AppendPrintf("(IUnknown*) 0x%p", aVariant.punkVal);
return true;
}
case VT_UNKNOWN | VT_BYREF: {
aOut.AppendPrintf("(IUnknown*) 0x%p", (aVariant.ppunkVal)[aIndex]);
return true;
}
case VT_VARIANT | VT_BYREF: {
return VariantToString((aVariant.pvarVal)[aIndex], aOut);
}
case VT_I4 | VT_BYREF: {
aOut.AppendPrintf("%ld", aVariant.plVal[aIndex]);
return true;
}
case VT_UI4 | VT_BYREF: {
aOut.AppendPrintf("%lu", aVariant.pulVal[aIndex]);
return true;
}
case VT_I4: {
aOut.AppendPrintf("%ld", aVariant.lVal);
return true;
}
case VT_UI4: {
aOut.AppendPrintf("%lu", 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();
TimeDuration duration = ts - TimeStamp::ProcessCreation();
return duration.ToMicroseconds();
}
void Logger::LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid,
IUnknown* aInterface, const TimeDuration* aOverheadDuration,
const TimeDuration* aGeckoDuration) {
if (FAILED(aResult)) {
return;
}
double elapsed = GetElapsedTime();
nsAutoCString strOverheadDuration;
if (aOverheadDuration) {
strOverheadDuration.AppendPrintf("%.3f",
aOverheadDuration->ToMicroseconds());
} else {
strOverheadDuration.AppendLiteral("(none)");
}
nsAutoCString strGeckoDuration;
if (aGeckoDuration) {
strGeckoDuration.AppendPrintf("%.3f", aGeckoDuration->ToMicroseconds());
} else {
strGeckoDuration.AppendLiteral("(none)");
}
nsPrintfCString line("%.3f\t%s\t%s\t0x%p\tIUnknown::QueryInterface\t([in] ",
elapsed, strOverheadDuration.get(),
strGeckoDuration.get(), 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%08lX\n", aInterface, aResult);
MutexAutoLock lock(mMutex);
mEntries.AppendElement(std::move(line));
mThread->Dispatch(
NewNonOwningRunnableMethod("Logger::Flush", this, &Logger::Flush),
NS_DISPATCH_NORMAL);
}
bool Logger::TryParamAsGuid(REFIID aIid, ICallFrame* aCallFrame,
const CALLFRAMEPARAMINFO& aParamInfo,
nsACString& aLine) {
if (aIid != IID_IServiceProvider) {
return false;
}
GUID** guid = reinterpret_cast<GUID**>(
static_cast<BYTE*>(aCallFrame->GetStackLocation()) +
aParamInfo.stackOffset);
if (!IsValidGUID(**guid)) {
return false;
}
WCHAR buf[39] = {0};
if (!StringFromGUID2(**guid, buf, mozilla::ArrayLength(buf))) {
return false;
}
aLine.AppendPrintf("%S", buf);
return true;
}
void Logger::CaptureFrame(ICallFrame* aCallFrame, IUnknown* aTargetInterface,
nsACString& aCapturedFrame) {
aCapturedFrame.Truncate();
// (1) Gather info about the call
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("0x%p\t%S::%S\t(", 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("{ ");
StringJoinAppend(line, ", "_ns,
mozilla::IntegerRange<LONG>(0, *lengthParam.plVal),
[this, &paramValue](nsACString& line, const LONG i) {
VariantToString(paramValue, line, i);
});
line.AppendLiteral(" }");
} else {
line.AppendPrintf("(GetParam failed with HRESULT 0x%08lX)", hr);
}
} else {
VariantToString(paramValue, line);
}
} else if (hr != DISP_E_BADVARTYPE ||
!TryParamAsGuid(callInfo.iid, aCallFrame, paramInfo, line)) {
line.AppendPrintf("(GetParam failed with HRESULT 0x%08lX)", hr);
}
if (paramIndex < callInfo.cParams - 1) {
line.AppendLiteral(", ");
}
}
line.AppendLiteral(")\t");
HRESULT callResult = aCallFrame->GetReturnValue();
line.AppendPrintf("0x%08lX\n", callResult);
aCapturedFrame = std::move(line);
}
void Logger::LogEvent(const nsACString& aCapturedFrame,
const TimeDuration& aOverheadDuration,
const TimeDuration& aGeckoDuration) {
double elapsed = GetElapsedTime();
nsPrintfCString line("%.3f\t%.3f\t%.3f\t%s", elapsed,
aOverheadDuration.ToMicroseconds(),
aGeckoDuration.ToMicroseconds(),
PromiseFlatCString(aCapturedFrame).get());
MutexAutoLock lock(mMutex);
mEntries.AppendElement(line);
mThread->Dispatch(
NewNonOwningRunnableMethod("Logger::Flush", 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 = std::move(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);
Unused << 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,
const TimeDuration* aOverheadDuration,
const TimeDuration* aGeckoDuration) {
if (!sLogger) {
return;
}
sLogger->LogQI(aResult, aTarget, aIid, aInterface, aOverheadDuration,
aGeckoDuration);
}
/* static */
void InterceptorLog::CaptureFrame(ICallFrame* aCallFrame,
IUnknown* aTargetInterface,
nsACString& aCapturedFrame) {
if (!sLogger) {
return;
}
sLogger->CaptureFrame(aCallFrame, aTargetInterface, aCapturedFrame);
}
/* static */
void InterceptorLog::Event(const nsACString& aCapturedFrame,
const TimeDuration& aOverheadDuration,
const TimeDuration& aGeckoDuration) {
if (!sLogger) {
return;
}
sLogger->LogEvent(aCapturedFrame, aOverheadDuration, aGeckoDuration);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,37 +0,0 @@
/* -*- 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
#include "mozilla/TimeStamp.h"
#include "nsString.h"
#include <windows.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,
const TimeDuration* aOverheadDuration = nullptr,
const TimeDuration* aGeckoDuration = nullptr);
static void CaptureFrame(ICallFrame* aCallFrame, IUnknown* aTarget,
nsACString& aCapturedFrame);
static void Event(const nsACString& aCapturedFrame,
const TimeDuration& aOverheadDuration,
const TimeDuration& aGeckoDuration);
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_InterceptorLog_h

View File

@ -1,697 +0,0 @@
/* -*- 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/MainThreadHandoff.h"
#include <utility>
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "mozilla/mscom/AgileReference.h"
#include "mozilla/mscom/InterceptorLog.h"
#include "mozilla/mscom/Registration.h"
#include "mozilla/mscom/Utils.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
using mozilla::DebugOnly;
using mozilla::Unused;
using mozilla::mscom::AgileReference;
namespace {
class MOZ_NON_TEMPORARY_CLASS InParamWalker : private ICallFrameWalker {
public:
InParamWalker() : mPreHandoff(true) {}
void SetHandoffDone() {
mPreHandoff = false;
mAgileRefsItr = mAgileRefs.begin();
}
HRESULT Walk(ICallFrame* aFrame) {
MOZ_ASSERT(aFrame);
if (!aFrame) {
return E_INVALIDARG;
}
return aFrame->WalkFrame(CALLFRAME_WALK_IN, this);
}
private:
// IUnknown
STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override {
if (!aOutInterface) {
return E_INVALIDARG;
}
*aOutInterface = nullptr;
if (aIid == IID_IUnknown || aIid == IID_ICallFrameWalker) {
*aOutInterface = static_cast<ICallFrameWalker*>(this);
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef() override { return 2; }
STDMETHODIMP_(ULONG) Release() override { return 1; }
// ICallFrameWalker
STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIn,
BOOL aOut) override {
MOZ_ASSERT(aIn);
if (!aIn) {
return E_UNEXPECTED;
}
IUnknown* origInterface = static_cast<IUnknown*>(*aInterface);
if (!origInterface) {
// Nothing to do
return S_OK;
}
if (mPreHandoff) {
mAgileRefs.AppendElement(AgileReference(aIid, origInterface));
return S_OK;
}
MOZ_ASSERT(mAgileRefsItr != mAgileRefs.end());
if (mAgileRefsItr == mAgileRefs.end()) {
return E_UNEXPECTED;
}
HRESULT hr = mAgileRefsItr->Resolve(aIid, aInterface);
MOZ_ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr)) {
++mAgileRefsItr;
}
return hr;
}
InParamWalker(const InParamWalker&) = delete;
InParamWalker(InParamWalker&&) = delete;
InParamWalker& operator=(const InParamWalker&) = delete;
InParamWalker& operator=(InParamWalker&&) = delete;
private:
bool mPreHandoff;
AutoTArray<AgileReference, 1> mAgileRefs;
nsTArray<AgileReference>::iterator mAgileRefsItr;
};
class HandoffRunnable : public mozilla::Runnable {
public:
explicit HandoffRunnable(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
: Runnable("HandoffRunnable"),
mCallFrame(aCallFrame),
mTargetInterface(aTargetInterface),
mResult(E_UNEXPECTED) {
DebugOnly<HRESULT> hr = mInParamWalker.Walk(aCallFrame);
MOZ_ASSERT(SUCCEEDED(hr));
}
NS_IMETHOD Run() override {
mInParamWalker.SetHandoffDone();
// We declare hr a DebugOnly because if mInParamWalker.Walk() fails, then
// mCallFrame->Invoke will fail anyway.
DebugOnly<HRESULT> hr = mInParamWalker.Walk(mCallFrame);
MOZ_ASSERT(SUCCEEDED(hr));
mResult = mCallFrame->Invoke(mTargetInterface);
return NS_OK;
}
HRESULT GetResult() const { return mResult; }
private:
ICallFrame* mCallFrame;
InParamWalker mInParamWalker;
IUnknown* mTargetInterface;
HRESULT mResult;
};
class MOZ_RAII SavedCallFrame final {
public:
explicit SavedCallFrame(mozilla::NotNull<ICallFrame*> aFrame)
: mCallFrame(aFrame) {
static const bool sIsInit = tlsFrame.init();
MOZ_ASSERT(sIsInit);
MOZ_ASSERT(!tlsFrame.get());
tlsFrame.set(this);
Unused << sIsInit;
}
~SavedCallFrame() {
MOZ_ASSERT(tlsFrame.get());
tlsFrame.set(nullptr);
}
HRESULT GetIidAndMethod(mozilla::NotNull<IID*> aIid,
mozilla::NotNull<ULONG*> aMethod) const {
return mCallFrame->GetIIDAndMethod(aIid, aMethod);
}
static const SavedCallFrame& Get() {
SavedCallFrame* saved = tlsFrame.get();
MOZ_ASSERT(saved);
return *saved;
}
SavedCallFrame(const SavedCallFrame&) = delete;
SavedCallFrame(SavedCallFrame&&) = delete;
SavedCallFrame& operator=(const SavedCallFrame&) = delete;
SavedCallFrame& operator=(SavedCallFrame&&) = delete;
private:
ICallFrame* mCallFrame;
private:
static MOZ_THREAD_LOCAL(SavedCallFrame*) tlsFrame;
};
MOZ_THREAD_LOCAL(SavedCallFrame*) SavedCallFrame::tlsFrame;
class MOZ_RAII LogEvent final {
public:
LogEvent() : mCallStart(mozilla::TimeStamp::Now()) {}
~LogEvent() {
if (mCapturedFrame.IsEmpty()) {
return;
}
mozilla::TimeStamp callEnd(mozilla::TimeStamp::Now());
mozilla::TimeDuration totalTime(callEnd - mCallStart);
mozilla::TimeDuration overhead(totalTime - mGeckoDuration -
mCaptureDuration);
mozilla::mscom::InterceptorLog::Event(mCapturedFrame, overhead,
mGeckoDuration);
}
void CaptureFrame(ICallFrame* aFrame, IUnknown* aTarget,
const mozilla::TimeDuration& aGeckoDuration) {
mozilla::TimeStamp captureStart(mozilla::TimeStamp::Now());
mozilla::mscom::InterceptorLog::CaptureFrame(aFrame, aTarget,
mCapturedFrame);
mGeckoDuration = aGeckoDuration;
mozilla::TimeStamp captureEnd(mozilla::TimeStamp::Now());
// Make sure that the time we spent in CaptureFrame isn't charged against
// overall overhead
mCaptureDuration = captureEnd - captureStart;
}
LogEvent(const LogEvent&) = delete;
LogEvent(LogEvent&&) = delete;
LogEvent& operator=(const LogEvent&) = delete;
LogEvent& operator=(LogEvent&&) = delete;
private:
mozilla::TimeStamp mCallStart;
mozilla::TimeDuration mGeckoDuration;
mozilla::TimeDuration mCaptureDuration;
nsAutoCString mCapturedFrame;
};
} // anonymous namespace
namespace mozilla {
namespace mscom {
/* static */
HRESULT MainThreadHandoff::Create(IHandlerProvider* aHandlerProvider,
IInterceptorSink** aOutput) {
RefPtr<MainThreadHandoff> handoff(new MainThreadHandoff(aHandlerProvider));
return handoff->QueryInterface(IID_IInterceptorSink, (void**)aOutput);
}
MainThreadHandoff::MainThreadHandoff(IHandlerProvider* aHandlerProvider)
: mRefCnt(0), mHandlerProvider(aHandlerProvider) {}
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 || riid == IID_IMainThreadHandoff) {
punk = static_cast<IMainThreadHandoff*>(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("MainThreadHandoff", self.forget());
}
}
return newRefCnt;
}
HRESULT
MainThreadHandoff::FixIServiceProvider(ICallFrame* aFrame) {
MOZ_ASSERT(aFrame);
CALLFRAMEPARAMINFO iidOutParamInfo;
HRESULT hr = aFrame->GetParamInfo(1, &iidOutParamInfo);
if (FAILED(hr)) {
return hr;
}
VARIANT varIfaceOut;
hr = aFrame->GetParam(2, &varIfaceOut);
if (FAILED(hr)) {
return hr;
}
MOZ_ASSERT(varIfaceOut.vt == (VT_UNKNOWN | VT_BYREF));
if (varIfaceOut.vt != (VT_UNKNOWN | VT_BYREF)) {
return DISP_E_BADVARTYPE;
}
IID** iidOutParam =
reinterpret_cast<IID**>(static_cast<BYTE*>(aFrame->GetStackLocation()) +
iidOutParamInfo.stackOffset);
return OnWalkInterface(**iidOutParam,
reinterpret_cast<void**>(varIfaceOut.ppunkVal), FALSE,
TRUE);
}
HRESULT
MainThreadHandoff::OnCall(ICallFrame* aFrame) {
LogEvent logEvent;
// (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<IUnknown> targetInterface;
hr = interceptor->GetTargetForIID(iid, targetInterface);
if (FAILED(hr)) {
return hr;
}
// (2) Execute the method call synchronously 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) Capture *before* wrapping outputs so that the log will contain pointers
// to the true target interface, not the wrapped ones.
logEvent.CaptureFrame(aFrame, targetInterface.get(), invoker.GetDuration());
// (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;
}
if (iid == IID_IServiceProvider) {
// The only possible method index for IID_IServiceProvider is for
// QueryService at index 3; its other methods are inherited from IUnknown
// and are not processed here.
MOZ_ASSERT(method == 3);
// (5) If our interface is IServiceProvider, we need to manually ensure
// that the correct IID is provided for the interface outparam in
// IServiceProvider::QueryService.
hr = FixIServiceProvider(aFrame);
if (FAILED(hr)) {
return hr;
}
} else if (const ArrayData* arrayData = FindArrayData(iid, method)) {
// (6) 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.
hr = FixArrayElements(aFrame, *arrayData);
if (FAILED(hr)) {
return hr;
}
} else {
SavedCallFrame savedFrame(WrapNotNull(aFrame));
// (7) 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::GetHandler(NotNull<CLSID*> aHandlerClsid) {
if (!mHandlerProvider) {
return E_NOTIMPL;
}
return mHandlerProvider->GetHandler(aHandlerClsid);
}
HRESULT
MainThreadHandoff::GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor,
NotNull<DWORD*> aOutPayloadSize) {
if (!mHandlerProvider) {
return E_NOTIMPL;
}
return mHandlerProvider->GetHandlerPayloadSize(aInterceptor, aOutPayloadSize);
}
HRESULT
MainThreadHandoff::WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
NotNull<IStream*> aStream) {
if (!mHandlerProvider) {
return E_NOTIMPL;
}
return mHandlerProvider->WriteHandlerPayload(aInterceptor, aStream);
}
REFIID
MainThreadHandoff::MarshalAs(REFIID aIid) {
if (!mHandlerProvider) {
return aIid;
}
return mHandlerProvider->MarshalAs(aIid);
}
HRESULT
MainThreadHandoff::DisconnectHandlerRemotes() {
if (!mHandlerProvider) {
return E_NOTIMPL;
}
return mHandlerProvider->DisconnectHandlerRemotes();
}
HRESULT
MainThreadHandoff::IsInterfaceMaybeSupported(REFIID aIid) {
if (!mHandlerProvider) {
return S_OK;
}
return mHandlerProvider->IsInterfaceMaybeSupported(aIid);
}
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<IUnknown> existingTarget;
hr = interceptor->GetTargetForIID(aIid, existingTarget);
if (SUCCEEDED(hr)) {
// We'll start by checking the raw pointers. If they are equal, then the
// objects are equal. OTOH, if they differ, we must compare their
// IUnknown pointers to know for sure.
bool areTargetsEqual = existingTarget.get() == origInterface.get();
if (!areTargetsEqual) {
// This check must be done on the main thread
auto checkFn = [&existingTarget, &origInterface,
&areTargetsEqual]() -> 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));
areTargetsEqual =
SUCCEEDED(hrExisting) && SUCCEEDED(hrNew) && unkExisting == unkNew;
};
MainThreadInvoker invoker;
invoker.Invoke(NS_NewRunnableFunction(
"MainThreadHandoff::OnWalkInterface", checkFn));
}
if (areTargetsEqual) {
// 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;
}
}
IID effectiveIid = aIid;
RefPtr<IHandlerProvider> payload;
if (mHandlerProvider) {
if (aIid == IID_IUnknown) {
const SavedCallFrame& curFrame = SavedCallFrame::Get();
IID callIid;
ULONG callMethod;
hr = curFrame.GetIidAndMethod(WrapNotNull(&callIid),
WrapNotNull(&callMethod));
if (FAILED(hr)) {
return hr;
}
effectiveIid =
mHandlerProvider->GetEffectiveOutParamIid(callIid, callMethod);
}
hr = mHandlerProvider->NewInstance(
effectiveIid, ToInterceptorTargetPtr(origInterface),
WrapNotNull((IHandlerProvider**)getter_AddRefs(payload)));
MOZ_ASSERT(SUCCEEDED(hr));
if (FAILED(hr)) {
return hr;
}
}
// Now create a new MainThreadHandoff wrapper...
RefPtr<IInterceptorSink> handoff;
hr = MainThreadHandoff::Create(payload, getter_AddRefs(handoff));
MOZ_ASSERT(SUCCEEDED(hr));
if (FAILED(hr)) {
return hr;
}
REFIID interceptorIid =
payload ? payload->MarshalAs(effectiveIid) : effectiveIid;
RefPtr<IUnknown> wrapped;
hr = Interceptor::Create(std::move(origInterface), handoff, interceptorIid,
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

@ -1,105 +0,0 @@
/* -*- 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 <utility>
#include "mozilla/Assertions.h"
#include "mozilla/Mutex.h"
#include "mozilla/mscom/Interceptor.h"
#include "mozilla/mscom/MainThreadInvoker.h"
#include "mozilla/mscom/Utils.h"
#include "nsTArray.h"
namespace mozilla {
namespace mscom {
// {9a907000-7829-47f1-80eb-f67a26f47b34}
DEFINE_GUID(IID_IMainThreadHandoff, 0x9a907000, 0x7829, 0x47f1, 0x80, 0xeb,
0xf6, 0x7a, 0x26, 0xf4, 0x7b, 0x34);
struct IMainThreadHandoff : public IInterceptorSink {
virtual STDMETHODIMP GetHandlerProvider(IHandlerProvider** aProvider) = 0;
};
struct ArrayData;
class MainThreadHandoff final : public IMainThreadHandoff,
public ICallFrameWalker {
public:
static HRESULT Create(IHandlerProvider* aHandlerProvider,
IInterceptorSink** aOutput);
template <typename Interface>
static HRESULT WrapInterface(STAUniquePtr<Interface> aTargetInterface,
Interface** aOutInterface) {
return WrapInterface<Interface>(std::move(aTargetInterface), nullptr,
aOutInterface);
}
template <typename Interface>
static HRESULT WrapInterface(STAUniquePtr<Interface> aTargetInterface,
IHandlerProvider* aHandlerProvider,
Interface** aOutInterface) {
MOZ_ASSERT(!IsProxy(aTargetInterface.get()));
RefPtr<IInterceptorSink> handoff;
HRESULT hr =
MainThreadHandoff::Create(aHandlerProvider, getter_AddRefs(handoff));
if (FAILED(hr)) {
return hr;
}
return CreateInterceptor(std::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;
STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor,
NotNull<DWORD*> aOutPayloadSize) override;
STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
NotNull<IStream*> aStream) override;
STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
STDMETHODIMP DisconnectHandlerRemotes() override;
STDMETHODIMP IsInterfaceMaybeSupported(REFIID aIid) override;
// IMainThreadHandoff
STDMETHODIMP GetHandlerProvider(IHandlerProvider** aProvider) override {
RefPtr<IHandlerProvider> provider = mHandlerProvider;
provider.forget(aProvider);
return mHandlerProvider ? S_OK : S_FALSE;
}
// ICallFrameWalker
STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam,
BOOL aIsOutParam) override;
private:
explicit MainThreadHandoff(IHandlerProvider* aHandlerProvider);
~MainThreadHandoff();
HRESULT FixArrayElements(ICallFrame* aFrame, const ArrayData& aArrayData);
HRESULT FixIServiceProvider(ICallFrame* aFrame);
private:
ULONG mRefCnt;
RefPtr<IWeakReference> mInterceptor;
RefPtr<IHandlerProvider> mHandlerProvider;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_MainThreadHandoff_h

View File

@ -1,177 +0,0 @@
/* -*- 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 "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ProfilerThreadState.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/mscom/SpinEvent.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Unused.h"
#include "private/prpriv.h" // For PR_GetThreadID
#include <winternl.h> // For NTSTATUS and NTAPI
namespace {
typedef NTSTATUS(NTAPI* NtTestAlertPtr)(VOID);
/**
* 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 SyncRunnable : public mozilla::Runnable {
public:
explicit SyncRunnable(already_AddRefed<nsIRunnable> aRunnable)
: mozilla::Runnable("MainThreadInvoker"), mRunnable(aRunnable) {
static const bool gotStatics = InitStatics();
MOZ_ASSERT(gotStatics);
mozilla::Unused << gotStatics;
}
~SyncRunnable() = default;
NS_IMETHOD Run() override {
if (mHasRun) {
// The APC already ran, so we have nothing to do.
return NS_OK;
}
// Run the pending APC in the queue.
MOZ_ASSERT(sNtTestAlert);
sNtTestAlert();
return NS_OK;
}
// This is called by MainThreadInvoker::MainThreadAPC.
void APCRun() {
mHasRun = true;
mozilla::TimeStamp runStart{mozilla::TimeStamp::Now()};
mRunnable->Run();
mozilla::TimeStamp runEnd{mozilla::TimeStamp::Now()};
mDuration = runEnd - runStart;
mEvent.Signal();
}
bool WaitUntilComplete() {
return mEvent.Wait(mozilla::mscom::MainThreadInvoker::GetTargetThread());
}
const mozilla::TimeDuration& GetDuration() const { return mDuration; }
private:
bool mHasRun = false;
nsCOMPtr<nsIRunnable> mRunnable;
mozilla::mscom::SpinEvent mEvent;
mozilla::TimeDuration mDuration;
static NtTestAlertPtr sNtTestAlert;
static bool InitStatics() {
sNtTestAlert = reinterpret_cast<NtTestAlertPtr>(
::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "NtTestAlert"));
MOZ_ASSERT(sNtTestAlert);
return sNtTestAlert;
}
};
NtTestAlertPtr SyncRunnable::sNtTestAlert = nullptr;
} // 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);
return !!sMainThread;
}
MainThreadInvoker::MainThreadInvoker() {
static const bool gotStatics = InitStatics();
MOZ_ASSERT(gotStatics);
mozilla::Unused << gotStatics;
}
bool MainThreadInvoker::Invoke(already_AddRefed<nsIRunnable>&& aRunnable) {
nsCOMPtr<nsIRunnable> runnable(std::move(aRunnable));
if (!runnable) {
return false;
}
if (NS_IsMainThread()) {
runnable->Run();
return true;
}
RefPtr<SyncRunnable> syncRunnable = new SyncRunnable(runnable.forget());
// The main thread could be either blocked on a condition variable waiting
// for a Gecko event, or it could be blocked waiting on a Windows HANDLE in
// IPC code (doing a sync message send). In the former case, we wake it by
// posting a Gecko runnable to the main thread. In the latter case, we wake
// it using an APC. However, the latter case doesn't happen very often now
// and APCs aren't otherwise run by the main thread. To ensure the
// SyncRunnable is cleaned up, we need both to run consistently.
// To do this, we:
// 1. Queue an APC which does the actual work.
// This ref gets released in MainThreadAPC when it runs.
SyncRunnable* syncRunnableRef = syncRunnable.get();
NS_ADDREF(syncRunnableRef);
if (!::QueueUserAPC(&MainThreadAPC, sMainThread,
reinterpret_cast<UINT_PTR>(syncRunnableRef))) {
return false;
}
// 2. Post a Gecko runnable (which always runs). If the APC hasn't run, the
// Gecko runnable runs it. Otherwise, it does nothing.
if (NS_FAILED(SchedulerGroup::Dispatch(TaskCategory::Other,
do_AddRef(syncRunnable)))) {
return false;
}
bool result = syncRunnable->WaitUntilComplete();
mDuration = syncRunnable->GetDuration();
return result;
}
/* static */ VOID CALLBACK MainThreadInvoker::MainThreadAPC(ULONG_PTR aParam) {
AUTO_PROFILER_THREAD_WAKE;
mozilla::BackgroundHangMonitor().NotifyActivity();
MOZ_ASSERT(NS_IsMainThread());
auto runnable = reinterpret_cast<SyncRunnable*>(aParam);
runnable->APCRun();
NS_RELEASE(runnable);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,56 +0,0 @@
/* -*- 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 <windows.h>
#include <utility>
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TimeStamp.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
class nsIRunnable;
namespace mozilla {
namespace mscom {
class MainThreadInvoker {
public:
MainThreadInvoker();
bool Invoke(already_AddRefed<nsIRunnable>&& aRunnable);
const TimeDuration& GetDuration() const { return mDuration; }
static HANDLE GetTargetThread() { return sMainThread; }
private:
TimeDuration mDuration;
static bool InitStatics();
static VOID CALLBACK MainThreadAPC(ULONG_PTR aParam);
static HANDLE sMainThread;
};
template <typename Class, typename... Args>
inline bool InvokeOnMainThread(const char* aName, Class* aObject,
void (Class::*aMethod)(Args...),
Args&&... aArgs) {
nsCOMPtr<nsIRunnable> runnable(NewNonOwningRunnableMethod<Args...>(
aName, aObject, aMethod, std::forward<Args>(aArgs)...));
MainThreadInvoker invoker;
return invoker.Invoke(runnable.forget());
}
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_MainThreadInvoker_h

View File

@ -1,411 +0,0 @@
/* -*- 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/Objref.h"
#include "mozilla/Assertions.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/UniquePtr.h"
#include <guiddef.h>
#include <objidl.h>
#include <winnt.h>
// {00000027-0000-0008-C000-000000000046}
static const GUID CLSID_AggStdMarshal = {
0x27, 0x0, 0x8, {0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}};
namespace {
#pragma pack(push, 1)
typedef uint64_t OID;
typedef uint64_t OXID;
typedef GUID IPID;
struct STDOBJREF {
uint32_t mFlags;
uint32_t mPublicRefs;
OXID mOxid;
OID mOid;
IPID mIpid;
};
enum STDOBJREF_FLAGS { SORF_PING = 0, SORF_NOPING = 0x1000 };
struct DUALSTRINGARRAY {
static size_t SizeFromNumEntries(const uint16_t aNumEntries) {
return sizeof(mNumEntries) + sizeof(mSecurityOffset) +
aNumEntries * sizeof(uint16_t);
}
size_t SizeOf() const { return SizeFromNumEntries(mNumEntries); }
uint16_t mNumEntries;
uint16_t mSecurityOffset;
uint16_t mStringArray[1]; // Length is mNumEntries
};
struct OBJREF_STANDARD {
static size_t SizeOfFixedLenHeader() { return sizeof(mStd); }
size_t SizeOf() const { return SizeOfFixedLenHeader() + mResAddr.SizeOf(); }
STDOBJREF mStd;
DUALSTRINGARRAY mResAddr;
};
struct OBJREF_HANDLER {
static size_t SizeOfFixedLenHeader() { return sizeof(mStd) + sizeof(mClsid); }
size_t SizeOf() const { return SizeOfFixedLenHeader() + mResAddr.SizeOf(); }
STDOBJREF mStd;
CLSID mClsid;
DUALSTRINGARRAY mResAddr;
};
struct OBJREF_CUSTOM {
static size_t SizeOfFixedLenHeader() {
return sizeof(mClsid) + sizeof(mCbExtension) + sizeof(mReserved);
}
CLSID mClsid;
uint32_t mCbExtension;
uint32_t mReserved;
uint8_t mPayload[1];
};
enum OBJREF_FLAGS {
OBJREF_TYPE_STANDARD = 0x00000001UL,
OBJREF_TYPE_HANDLER = 0x00000002UL,
OBJREF_TYPE_CUSTOM = 0x00000004UL,
OBJREF_TYPE_EXTENDED = 0x00000008UL,
};
struct OBJREF {
static size_t SizeOfFixedLenHeader(OBJREF_FLAGS aFlags) {
size_t size = sizeof(mSignature) + sizeof(mFlags) + sizeof(mIid);
switch (aFlags) {
case OBJREF_TYPE_STANDARD:
size += OBJREF_STANDARD::SizeOfFixedLenHeader();
break;
case OBJREF_TYPE_HANDLER:
size += OBJREF_HANDLER::SizeOfFixedLenHeader();
break;
case OBJREF_TYPE_CUSTOM:
size += OBJREF_CUSTOM::SizeOfFixedLenHeader();
break;
default:
MOZ_ASSERT_UNREACHABLE("Unsupported OBJREF type");
return 0;
}
return size;
}
size_t SizeOf() const {
size_t size = sizeof(mSignature) + sizeof(mFlags) + sizeof(mIid);
switch (mFlags) {
case OBJREF_TYPE_STANDARD:
size += mObjRefStd.SizeOf();
break;
case OBJREF_TYPE_HANDLER:
size += mObjRefHandler.SizeOf();
break;
default:
MOZ_ASSERT_UNREACHABLE("Unsupported OBJREF type");
return 0;
}
return size;
}
uint32_t mSignature;
uint32_t mFlags;
IID mIid;
union {
OBJREF_STANDARD mObjRefStd;
OBJREF_HANDLER mObjRefHandler;
OBJREF_CUSTOM mObjRefCustom;
// There are others but we're not supporting them here
};
};
enum OBJREF_SIGNATURES { OBJREF_SIGNATURE = 0x574F454DUL };
#pragma pack(pop)
struct ByteArrayDeleter {
void operator()(void* aPtr) { delete[] reinterpret_cast<uint8_t*>(aPtr); }
};
template <typename T>
using VarStructUniquePtr = mozilla::UniquePtr<T, ByteArrayDeleter>;
} // anonymous namespace
namespace mozilla {
namespace mscom {
bool StripHandlerFromOBJREF(NotNull<IStream*> aStream, const uint64_t aStartPos,
const uint64_t aEndPos) {
// Ensure that the current stream position is set to the beginning
LARGE_INTEGER seekTo;
seekTo.QuadPart = aStartPos;
HRESULT hr = aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
if (FAILED(hr)) {
return false;
}
ULONG bytesRead;
uint32_t signature;
hr = aStream->Read(&signature, sizeof(signature), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(signature) ||
signature != OBJREF_SIGNATURE) {
return false;
}
uint32_t type;
hr = aStream->Read(&type, sizeof(type), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(type)) {
return false;
}
if (type != OBJREF_TYPE_HANDLER) {
// If we're not a handler then just seek to the end of the OBJREF and return
// success; there is nothing left to do.
seekTo.QuadPart = aEndPos;
return SUCCEEDED(aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr));
}
IID iid;
hr = aStream->Read(&iid, sizeof(iid), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(iid) || !IsValidGUID(iid)) {
return false;
}
// Seek past fixed-size STDOBJREF and CLSID
seekTo.QuadPart = sizeof(STDOBJREF) + sizeof(CLSID);
hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
if (FAILED(hr)) {
return false;
}
uint16_t numEntries;
hr = aStream->Read(&numEntries, sizeof(numEntries), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(numEntries)) {
return false;
}
// We'll try to use a stack buffer if resAddrSize <= kMinDualStringArraySize
const uint32_t kMinDualStringArraySize = 12;
uint16_t staticResAddrBuf[kMinDualStringArraySize / sizeof(uint16_t)];
size_t resAddrSize = DUALSTRINGARRAY::SizeFromNumEntries(numEntries);
DUALSTRINGARRAY* resAddr;
VarStructUniquePtr<DUALSTRINGARRAY> dynamicResAddrBuf;
if (resAddrSize <= kMinDualStringArraySize) {
resAddr = reinterpret_cast<DUALSTRINGARRAY*>(staticResAddrBuf);
} else {
dynamicResAddrBuf.reset(
reinterpret_cast<DUALSTRINGARRAY*>(new uint8_t[resAddrSize]));
resAddr = dynamicResAddrBuf.get();
}
resAddr->mNumEntries = numEntries;
// Because we've already read numEntries
ULONG bytesToRead = resAddrSize - sizeof(numEntries);
hr = aStream->Read(&resAddr->mSecurityOffset, bytesToRead, &bytesRead);
if (FAILED(hr) || bytesRead != bytesToRead) {
return false;
}
// Signature doesn't change so we'll seek past that
seekTo.QuadPart = aStartPos + sizeof(signature);
hr = aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
if (FAILED(hr)) {
return false;
}
ULONG bytesWritten;
uint32_t newType = OBJREF_TYPE_STANDARD;
hr = aStream->Write(&newType, sizeof(newType), &bytesWritten);
if (FAILED(hr) || bytesWritten != sizeof(newType)) {
return false;
}
// Skip past IID and STDOBJREF since those don't change
seekTo.QuadPart = sizeof(IID) + sizeof(STDOBJREF);
hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
if (FAILED(hr)) {
return false;
}
hr = aStream->Write(resAddr, resAddrSize, &bytesWritten);
if (FAILED(hr) || bytesWritten != resAddrSize) {
return false;
}
// The difference between a OBJREF_STANDARD and an OBJREF_HANDLER is
// sizeof(CLSID), so we'll zero out the remaining bytes.
hr = aStream->Write(&CLSID_NULL, sizeof(CLSID), &bytesWritten);
if (FAILED(hr) || bytesWritten != sizeof(CLSID)) {
return false;
}
// Back up to the end of the tweaked OBJREF.
// There are now sizeof(CLSID) less bytes.
// Bug 1403180: Using -sizeof(CLSID) with a relative seek sometimes
// doesn't work on Windows 7.
// It succeeds, but doesn't seek the stream for some unknown reason.
// Use an absolute seek instead.
seekTo.QuadPart = aEndPos - sizeof(CLSID);
return SUCCEEDED(aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr));
}
uint32_t GetOBJREFSize(NotNull<IStream*> aStream) {
// Make a clone so that we don't manipulate aStream's seek pointer
RefPtr<IStream> cloned;
HRESULT hr = aStream->Clone(getter_AddRefs(cloned));
if (FAILED(hr)) {
return 0;
}
uint32_t accumulatedSize = 0;
ULONG bytesRead;
uint32_t signature;
hr = cloned->Read(&signature, sizeof(signature), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(signature) ||
signature != OBJREF_SIGNATURE) {
return 0;
}
accumulatedSize += bytesRead;
uint32_t type;
hr = cloned->Read(&type, sizeof(type), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(type)) {
return 0;
}
accumulatedSize += bytesRead;
IID iid;
hr = cloned->Read(&iid, sizeof(iid), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(iid) || !IsValidGUID(iid)) {
return 0;
}
accumulatedSize += bytesRead;
LARGE_INTEGER seekTo;
if (type == OBJREF_TYPE_CUSTOM) {
CLSID clsid;
hr = cloned->Read(&clsid, sizeof(CLSID), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(CLSID)) {
return 0;
}
if (clsid != CLSID_StdMarshal && clsid != CLSID_AggStdMarshal) {
// We can only calulate the size if the payload is a standard OBJREF as
// identified by clsid being CLSID_StdMarshal or CLSID_AggStdMarshal.
// (CLSID_AggStdMarshal, the aggregated standard marshaler, is used when
// the handler marshals an interface.)
return 0;
}
accumulatedSize += bytesRead;
seekTo.QuadPart =
sizeof(OBJREF_CUSTOM::mCbExtension) + sizeof(OBJREF_CUSTOM::mReserved);
hr = cloned->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
if (FAILED(hr)) {
return 0;
}
accumulatedSize += seekTo.LowPart;
uint32_t payloadLen = GetOBJREFSize(WrapNotNull(cloned.get()));
if (!payloadLen) {
return 0;
}
accumulatedSize += payloadLen;
return accumulatedSize;
}
switch (type) {
case OBJREF_TYPE_STANDARD:
seekTo.QuadPart = OBJREF_STANDARD::SizeOfFixedLenHeader();
break;
case OBJREF_TYPE_HANDLER:
seekTo.QuadPart = OBJREF_HANDLER::SizeOfFixedLenHeader();
break;
default:
return 0;
}
hr = cloned->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
if (FAILED(hr)) {
return 0;
}
accumulatedSize += seekTo.LowPart;
uint16_t numEntries;
hr = cloned->Read(&numEntries, sizeof(numEntries), &bytesRead);
if (FAILED(hr) || bytesRead != sizeof(numEntries)) {
return 0;
}
accumulatedSize += DUALSTRINGARRAY::SizeFromNumEntries(numEntries);
return accumulatedSize;
}
bool SetIID(NotNull<IStream*> aStream, const uint64_t aStart, REFIID aNewIid) {
ULARGE_INTEGER initialStreamPos;
LARGE_INTEGER seekTo;
seekTo.QuadPart = 0LL;
HRESULT hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, &initialStreamPos);
if (FAILED(hr)) {
return false;
}
auto resetStreamPos = MakeScopeExit([&]() {
seekTo.QuadPart = initialStreamPos.QuadPart;
hr = aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
});
seekTo.QuadPart =
aStart + sizeof(OBJREF::mSignature) + sizeof(OBJREF::mFlags);
hr = aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
if (FAILED(hr)) {
return false;
}
ULONG bytesWritten;
hr = aStream->Write(&aNewIid, sizeof(IID), &bytesWritten);
return SUCCEEDED(hr) && bytesWritten == sizeof(IID);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,53 +0,0 @@
/* -*- 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_Objref_h
#define mozilla_mscom_Objref_h
#include "mozilla/NotNull.h"
#include "mozilla/RefPtr.h"
#include <guiddef.h>
struct IStream;
namespace mozilla {
namespace mscom {
/**
* Given a buffer containing a serialized proxy to an interface with a handler,
* this function strips out the handler and converts it to a standard one.
* @param aStream IStream containing a serialized proxy.
* There should be nothing else written to the stream past the
* current OBJREF.
* @param aStart Absolute position of the beginning of the OBJREF.
* @param aEnd Absolute position of the end of the OBJREF.
* @return true if the handler was successfully stripped, otherwise false.
*/
bool StripHandlerFromOBJREF(NotNull<IStream*> aStream, const uint64_t aStart,
const uint64_t aEnd);
/**
* Given a buffer containing a serialized proxy to an interface, this function
* returns the length of the serialized data.
* @param aStream IStream containing a serialized proxy. The stream pointer
* must be positioned at the beginning of the OBJREF.
* @return The size of the serialized proxy, or 0 on error.
*/
uint32_t GetOBJREFSize(NotNull<IStream*> aStream);
/**
* Overrides the IID in a serialized proxy with the specified IID.
* @param aStream Pointer to a stream containing a serialized proxy.
* @param aStart Offset to the beginning of the serialized proxy within aStream.
* @param aNewIid The replacement IID to apply to the serialized proxy.
*/
bool SetIID(NotNull<IStream*> aStream, const uint64_t aStart, REFIID aNewIid);
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Objref_h

View File

@ -1,393 +0,0 @@
/* -*- 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/PassthruProxy.h"
#include "mozilla/mscom/ProxyStream.h"
#include "VTableBuilder.h"
// {96EF5801-CE6D-416E-A50A-0C2959AEAE1C}
static const GUID CLSID_PassthruProxy = {
0x96ef5801, 0xce6d, 0x416e, {0xa5, 0xa, 0xc, 0x29, 0x59, 0xae, 0xae, 0x1c}};
namespace mozilla {
namespace mscom {
PassthruProxy::PassthruProxy()
: mRefCnt(0),
mWrappedIid(),
mVTableSize(0),
mVTable(nullptr),
mForgetPreservedStream(false) {}
PassthruProxy::PassthruProxy(ProxyStream::Environment* aEnv, REFIID aIidToWrap,
uint32_t aVTableSize,
NotNull<IUnknown*> aObjToWrap)
: mRefCnt(0),
mWrappedIid(aIidToWrap),
mVTableSize(aVTableSize),
mVTable(nullptr),
mForgetPreservedStream(false) {
ProxyStream proxyStream(aIidToWrap, aObjToWrap, aEnv,
ProxyStreamFlags::ePreservable);
mPreservedStream = proxyStream.GetPreservedStream();
MOZ_ASSERT(mPreservedStream);
}
PassthruProxy::~PassthruProxy() {
if (mForgetPreservedStream) {
// We want to release the ref without clearing marshal data
IStream* stream = mPreservedStream.release();
stream->Release();
}
if (mVTable) {
DeleteNullVTable(mVTable);
}
}
HRESULT
PassthruProxy::QueryProxyInterface(void** aOutInterface) {
// Even though we don't really provide the methods for the interface that
// we are proxying, we need to support it in QueryInterface. Instead we
// return an interface that, other than IUnknown, contains nullptr for all of
// its vtable entires. Obviously this interface is not intended to actually
// be called, it just has to be there.
if (!mVTable) {
MOZ_ASSERT(mVTableSize);
mVTable = BuildNullVTable(static_cast<IMarshal*>(this), mVTableSize);
MOZ_ASSERT(mVTable);
}
*aOutInterface = mVTable;
mVTable->AddRef();
return S_OK;
}
HRESULT
PassthruProxy::QueryInterface(REFIID aIid, void** aOutInterface) {
if (!aOutInterface) {
return E_INVALIDARG;
}
*aOutInterface = nullptr;
if (aIid == IID_IUnknown || aIid == IID_IMarshal) {
RefPtr<IMarshal> ptr(this);
ptr.forget(aOutInterface);
return S_OK;
}
if (!IsInitialMarshal()) {
// We implement IClientSecurity so that IsProxy() recognizes us as such
if (aIid == IID_IClientSecurity) {
RefPtr<IClientSecurity> ptr(this);
ptr.forget(aOutInterface);
return S_OK;
}
if (aIid == mWrappedIid) {
return QueryProxyInterface(aOutInterface);
}
}
return E_NOINTERFACE;
}
ULONG
PassthruProxy::AddRef() { return ++mRefCnt; }
ULONG
PassthruProxy::Release() {
ULONG result = --mRefCnt;
if (!result) {
delete this;
}
return result;
}
HRESULT
PassthruProxy::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) {
if (!pCid) {
return E_INVALIDARG;
}
if (IsInitialMarshal()) {
// To properly use this class we need to be using TABLESTRONG marshaling
MOZ_ASSERT(mshlflags & MSHLFLAGS_TABLESTRONG);
// When we're marshaling for the first time, we identify ourselves as the
// class to use for unmarshaling.
*pCid = CLSID_PassthruProxy;
} else {
// Subsequent marshals use the standard marshaler.
*pCid = CLSID_StdMarshal;
}
return S_OK;
}
HRESULT
PassthruProxy::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) {
STATSTG statstg;
HRESULT hr;
if (!IsInitialMarshal()) {
// If we are not the initial marshal then we are just copying mStream out
// to the marshal stream, so we just use mStream's size.
hr = mStream->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr)) {
return hr;
}
*pSize = statstg.cbSize.LowPart;
return hr;
}
// To properly use this class we need to be using TABLESTRONG marshaling
MOZ_ASSERT(mshlflags & MSHLFLAGS_TABLESTRONG);
if (!mPreservedStream) {
return E_POINTER;
}
hr = mPreservedStream->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr)) {
return hr;
}
*pSize = statstg.cbSize.LowPart + sizeof(mVTableSize) + sizeof(mWrappedIid);
return hr;
}
HRESULT
PassthruProxy::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) {
MOZ_ASSERT(riid == mWrappedIid);
if (riid != mWrappedIid) {
return E_NOINTERFACE;
}
MOZ_ASSERT(pv == mVTable);
if (pv != mVTable) {
return E_INVALIDARG;
}
HRESULT hr;
RefPtr<IStream> cloned;
if (IsInitialMarshal()) {
// To properly use this class we need to be using TABLESTRONG marshaling
MOZ_ASSERT(mshlflags & MSHLFLAGS_TABLESTRONG);
if (!mPreservedStream) {
return E_POINTER;
}
// We write out the vtable size and the IID so that the wrapped proxy knows
// how to build its vtable on the content side.
ULONG bytesWritten;
hr = pStm->Write(&mVTableSize, sizeof(mVTableSize), &bytesWritten);
if (FAILED(hr)) {
return hr;
}
if (bytesWritten != sizeof(mVTableSize)) {
return E_UNEXPECTED;
}
hr = pStm->Write(&mWrappedIid, sizeof(mWrappedIid), &bytesWritten);
if (FAILED(hr)) {
return hr;
}
if (bytesWritten != sizeof(mWrappedIid)) {
return E_UNEXPECTED;
}
hr = mPreservedStream->Clone(getter_AddRefs(cloned));
} else {
hr = mStream->Clone(getter_AddRefs(cloned));
}
if (FAILED(hr)) {
return hr;
}
STATSTG statstg;
hr = cloned->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr)) {
return hr;
}
// Copy the proxy data
hr = cloned->CopyTo(pStm, statstg.cbSize, nullptr, nullptr);
if (SUCCEEDED(hr) && IsInitialMarshal() && mPreservedStream &&
(mshlflags & MSHLFLAGS_TABLESTRONG)) {
// If we have successfully copied mPreservedStream at least once for a
// MSHLFLAGS_TABLESTRONG marshal, then we want to forget our reference to
// it. This is because the COM runtime will manage it from here on out.
mForgetPreservedStream = true;
}
return hr;
}
HRESULT
PassthruProxy::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
// Read out the interface info that we copied during marshaling
ULONG bytesRead;
HRESULT hr = pStm->Read(&mVTableSize, sizeof(mVTableSize), &bytesRead);
if (FAILED(hr)) {
return hr;
}
if (bytesRead != sizeof(mVTableSize)) {
return E_UNEXPECTED;
}
hr = pStm->Read(&mWrappedIid, sizeof(mWrappedIid), &bytesRead);
if (FAILED(hr)) {
return hr;
}
if (bytesRead != sizeof(mWrappedIid)) {
return E_UNEXPECTED;
}
// Now we copy the proxy inside pStm into mStream
hr = CopySerializedProxy(pStm, getter_AddRefs(mStream));
if (FAILED(hr)) {
return hr;
}
return QueryInterface(riid, ppv);
}
HRESULT
PassthruProxy::ReleaseMarshalData(IStream* pStm) {
if (!IsInitialMarshal()) {
return S_OK;
}
if (!pStm) {
return E_INVALIDARG;
}
if (mPreservedStream) {
// If we still have mPreservedStream, then simply clearing it will release
// its marshal data automagically.
mPreservedStream = nullptr;
return S_OK;
}
// Skip past the metadata that we wrote during initial marshaling.
LARGE_INTEGER seekTo;
seekTo.QuadPart = sizeof(mVTableSize) + sizeof(mWrappedIid);
HRESULT hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
if (FAILED(hr)) {
return hr;
}
// Now release the "inner" marshal data
return ::CoReleaseMarshalData(pStm);
}
HRESULT
PassthruProxy::DisconnectObject(DWORD dwReserved) { return S_OK; }
// The remainder of this code is just boilerplate COM stuff that provides the
// association between CLSID_PassthruProxy and the PassthruProxy class itself.
class PassthruProxyClassObject final : public IClassFactory {
public:
PassthruProxyClassObject();
// IUnknown
STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IClassFactory
STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aIid,
void** aOutObject) override;
STDMETHODIMP LockServer(BOOL aLock) override;
private:
~PassthruProxyClassObject() = default;
Atomic<ULONG> mRefCnt;
};
PassthruProxyClassObject::PassthruProxyClassObject() : mRefCnt(0) {}
HRESULT
PassthruProxyClassObject::QueryInterface(REFIID aIid, void** aOutInterface) {
if (!aOutInterface) {
return E_INVALIDARG;
}
*aOutInterface = nullptr;
if (aIid == IID_IUnknown || aIid == IID_IClassFactory) {
RefPtr<IClassFactory> ptr(this);
ptr.forget(aOutInterface);
return S_OK;
}
return E_NOINTERFACE;
}
ULONG
PassthruProxyClassObject::AddRef() { return ++mRefCnt; }
ULONG
PassthruProxyClassObject::Release() {
ULONG result = --mRefCnt;
if (!result) {
delete this;
}
return result;
}
HRESULT
PassthruProxyClassObject::CreateInstance(IUnknown* aOuter, REFIID aIid,
void** aOutObject) {
// We don't expect to aggregate
MOZ_ASSERT(!aOuter);
if (aOuter) {
return E_INVALIDARG;
}
RefPtr<PassthruProxy> ptr(new PassthruProxy());
return ptr->QueryInterface(aIid, aOutObject);
}
HRESULT
PassthruProxyClassObject::LockServer(BOOL aLock) {
// No-op since xul.dll is always in memory
return S_OK;
}
/* static */
HRESULT PassthruProxy::Register() {
DWORD cookie;
RefPtr<IClassFactory> classObj(new PassthruProxyClassObject());
return ::CoRegisterClassObject(CLSID_PassthruProxy, classObj,
CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE,
&cookie);
}
} // namespace mscom
} // namespace mozilla
HRESULT
RegisterPassthruProxy() { return mozilla::mscom::PassthruProxy::Register(); }

View File

@ -1,127 +0,0 @@
/* -*- 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_PassthruProxy_h
#define mozilla_mscom_PassthruProxy_h
#include "mozilla/Atomics.h"
#include "mozilla/mscom/ProxyStream.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/NotNull.h"
#if defined(MOZ_SANDBOX)
# include "mozilla/SandboxSettings.h"
#endif // defined(MOZ_SANDBOX)
#include <objbase.h>
namespace mozilla {
namespace mscom {
namespace detail {
template <typename Iface>
struct VTableSizer;
template <>
struct VTableSizer<IDispatch> {
enum { Size = 7 };
};
} // namespace detail
class PassthruProxy final : public IMarshal, public IClientSecurity {
public:
template <typename Iface>
static RefPtr<Iface> Wrap(NotNull<Iface*> aIn) {
static_assert(detail::VTableSizer<Iface>::Size >= 3, "VTable too small");
#if defined(MOZ_SANDBOX)
if (mozilla::GetEffectiveContentSandboxLevel() < 3) {
// The sandbox isn't strong enough to be a problem; no wrapping required
return aIn.get();
}
typename detail::EnvironmentSelector<Iface>::Type env;
RefPtr<PassthruProxy> passthru(new PassthruProxy(
&env, __uuidof(Iface), detail::VTableSizer<Iface>::Size, aIn));
RefPtr<Iface> result;
if (FAILED(passthru->QueryProxyInterface(getter_AddRefs(result)))) {
return nullptr;
}
return result;
#else
// No wrapping required
return aIn.get();
#endif // defined(MOZ_SANDBOX)
}
static HRESULT Register();
PassthruProxy();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IMarshal
STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) override;
STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) override;
STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) override;
STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid,
void** ppv) override;
STDMETHODIMP ReleaseMarshalData(IStream* pStm) override;
STDMETHODIMP DisconnectObject(DWORD dwReserved) override;
// IClientSecurity - we don't actually implement this interface, but its
// presence signals to mscom::IsProxy() that we are a proxy.
STDMETHODIMP QueryBlanket(IUnknown* aProxy, DWORD* aAuthnSvc,
DWORD* aAuthzSvc, OLECHAR** aSrvPrincName,
DWORD* aAuthnLevel, DWORD* aImpLevel,
void** aAuthInfo, DWORD* aCapabilities) override {
return E_NOTIMPL;
}
STDMETHODIMP SetBlanket(IUnknown* aProxy, DWORD aAuthnSvc, DWORD aAuthzSvc,
OLECHAR* aSrvPrincName, DWORD aAuthnLevel,
DWORD aImpLevel, void* aAuthInfo,
DWORD aCapabilities) override {
return E_NOTIMPL;
}
STDMETHODIMP CopyProxy(IUnknown* aProxy, IUnknown** aOutCopy) override {
return E_NOTIMPL;
}
private:
PassthruProxy(ProxyStream::Environment* aEnv, REFIID aIidToWrap,
uint32_t aVTableSize, NotNull<IUnknown*> aObjToWrap);
~PassthruProxy();
bool IsInitialMarshal() const { return !mStream; }
HRESULT QueryProxyInterface(void** aOutInterface);
Atomic<ULONG> mRefCnt;
IID mWrappedIid;
PreservedStreamPtr mPreservedStream;
RefPtr<IStream> mStream;
uint32_t mVTableSize;
IUnknown* mVTable;
bool mForgetPreservedStream;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_PassthruProxy_h

View File

@ -1,349 +0,0 @@
/* -*- 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 <utility>
#include "mozilla/mscom/EnsureMTA.h"
#include "mozilla/mscom/ProxyStream.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/mscom/Objref.h"
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"
#include "RegistrationAnnotator.h"
#include <windows.h>
#include <objbase.h>
#include <shlwapi.h>
namespace mozilla {
namespace mscom {
ProxyStream::ProxyStream()
: mGlobalLockedBuf(nullptr),
mHGlobal(nullptr),
mBufSize(0),
mPreserveStream(false) {}
// GetBuffer() fails with this variant, but that's okay because we're just
// reconstructing the stream from a buffer anyway.
ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
const int aInitBufSize, Environment* aEnv)
: mGlobalLockedBuf(nullptr),
mHGlobal(nullptr),
mBufSize(aInitBufSize),
mPreserveStream(false) {
CrashReporter::Annotation kCrashReportKey =
CrashReporter::Annotation::ProxyStreamUnmarshalStatus;
if (!aInitBufSize) {
CrashReporter::AnnotateCrashReport(kCrashReportKey, "!aInitBufSize"_ns);
// We marshaled a nullptr. Nothing else to do here.
return;
}
HRESULT createStreamResult =
CreateStream(aInitBuf, aInitBufSize, getter_AddRefs(mStream));
if (FAILED(createStreamResult)) {
nsPrintfCString hrAsStr("0x%08lX", createStreamResult);
CrashReporter::AnnotateCrashReport(kCrashReportKey, hrAsStr);
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) {
CrashReporter::AnnotateCrashReport(kCrashReportKey, "!mStream"_ns);
return;
}
HRESULT unmarshalResult = S_OK;
// 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 = [this, &unmarshalResult, &aIID, aEnv]() -> void {
if (aEnv) {
bool pushOk = aEnv->Push();
MOZ_DIAGNOSTIC_ASSERT(pushOk);
if (!pushOk) {
return;
}
}
auto popEnv = MakeScopeExit([aEnv]() -> void {
if (!aEnv) {
return;
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
bool popOk =
#endif
aEnv->Pop();
MOZ_DIAGNOSTIC_ASSERT(popOk);
});
unmarshalResult = ::CoUnmarshalInterface(mStream, aIID,
getter_AddRefs(mUnmarshaledProxy));
MOZ_ASSERT(SUCCEEDED(unmarshalResult));
};
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);
}
mStream = nullptr;
if (FAILED(unmarshalResult) || !mUnmarshaledProxy) {
nsPrintfCString hrAsStr("0x%08lX", unmarshalResult);
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::CoUnmarshalInterfaceResult, hrAsStr);
AnnotateInterfaceRegistration(aIID);
if (!mUnmarshaledProxy) {
CrashReporter::AnnotateCrashReport(kCrashReportKey,
"!mUnmarshaledProxy"_ns);
}
}
}
ProxyStream::ProxyStream(ProxyStream&& aOther)
: mGlobalLockedBuf(nullptr),
mHGlobal(nullptr),
mBufSize(0),
mPreserveStream(false) {
*this = std::move(aOther);
}
ProxyStream& ProxyStream::operator=(ProxyStream&& aOther) {
if (mHGlobal && mGlobalLockedBuf) {
DebugOnly<BOOL> result = ::GlobalUnlock(mHGlobal);
MOZ_ASSERT(!result && ::GetLastError() == NO_ERROR);
}
mStream = std::move(aOther.mStream);
mGlobalLockedBuf = aOther.mGlobalLockedBuf;
aOther.mGlobalLockedBuf = nullptr;
// ::GlobalFree() was called implicitly when mStream was replaced.
mHGlobal = aOther.mHGlobal;
aOther.mHGlobal = nullptr;
mBufSize = aOther.mBufSize;
aOther.mBufSize = 0;
mUnmarshaledProxy = std::move(aOther.mUnmarshaledProxy);
mPreserveStream = aOther.mPreserveStream;
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
}
// If this assert triggers then we will be leaking a marshaled proxy!
// Call GetPreservedStream to obtain a preservable stream and then save it
// until the proxy is no longer needed.
MOZ_ASSERT(!mPreserveStream);
}
const BYTE* ProxyStream::GetBuffer(int& aReturnedBufSize) const {
aReturnedBufSize = 0;
if (!mStream) {
return nullptr;
}
if (!mGlobalLockedBuf) {
return nullptr;
}
aReturnedBufSize = mBufSize;
return mGlobalLockedBuf;
}
PreservedStreamPtr ProxyStream::GetPreservedStream() {
MOZ_ASSERT(mStream);
MOZ_ASSERT(mHGlobal);
if (!mStream || !mPreserveStream) {
return nullptr;
}
// Clone the stream so that the result has a distinct seek pointer.
RefPtr<IStream> cloned;
HRESULT hr = mStream->Clone(getter_AddRefs(cloned));
if (FAILED(hr)) {
return nullptr;
}
// Ensure the stream is rewound. We do this because CoReleaseMarshalData needs
// the stream to be pointing to the beginning of the marshal data.
LARGE_INTEGER pos;
pos.QuadPart = 0LL;
hr = cloned->Seek(pos, STREAM_SEEK_SET, nullptr);
if (FAILED(hr)) {
return nullptr;
}
mPreserveStream = false;
return ToPreservedStreamPtr(std::move(cloned));
}
bool ProxyStream::GetInterface(void** aOutInterface) {
// We should not have a locked buffer on this side
MOZ_ASSERT(!mGlobalLockedBuf);
MOZ_ASSERT(aOutInterface);
if (!aOutInterface) {
return false;
}
*aOutInterface = mUnmarshaledProxy.release();
return true;
}
ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject, Environment* aEnv,
ProxyStreamFlags aFlags)
: mGlobalLockedBuf(nullptr),
mHGlobal(nullptr),
mBufSize(0),
mPreserveStream(aFlags & ProxyStreamFlags::ePreservable) {
if (!aObject) {
return;
}
RefPtr<IStream> stream;
HGLOBAL hglobal = NULL;
int streamSize = 0;
DWORD mshlFlags = mPreserveStream ? MSHLFLAGS_TABLESTRONG : MSHLFLAGS_NORMAL;
HRESULT createStreamResult = S_OK;
HRESULT marshalResult = S_OK;
HRESULT statResult = S_OK;
HRESULT getHGlobalResult = S_OK;
auto marshalFn = [&aIID, aObject, mshlFlags, &stream, &streamSize, &hglobal,
&createStreamResult, &marshalResult, &statResult,
&getHGlobalResult, aEnv]() -> void {
if (aEnv) {
bool pushOk = aEnv->Push();
MOZ_DIAGNOSTIC_ASSERT(pushOk);
if (!pushOk) {
return;
}
}
auto popEnv = MakeScopeExit([aEnv]() -> void {
if (!aEnv) {
return;
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
bool popOk =
#endif
aEnv->Pop();
MOZ_DIAGNOSTIC_ASSERT(popOk);
});
createStreamResult =
::CreateStreamOnHGlobal(nullptr, TRUE, getter_AddRefs(stream));
if (FAILED(createStreamResult)) {
return;
}
marshalResult = ::CoMarshalInterface(stream, aIID, aObject, MSHCTX_LOCAL,
nullptr, mshlFlags);
MOZ_ASSERT(marshalResult != E_INVALIDARG);
if (FAILED(marshalResult)) {
return;
}
STATSTG statstg;
statResult = stream->Stat(&statstg, STATFLAG_NONAME);
if (SUCCEEDED(statResult)) {
streamSize = static_cast<int>(statstg.cbSize.LowPart);
} else {
return;
}
getHGlobalResult = ::GetHGlobalFromStream(stream, &hglobal);
MOZ_ASSERT(SUCCEEDED(getHGlobalResult));
};
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);
}
if (FAILED(createStreamResult)) {
nsPrintfCString hrAsStr("0x%08lX", createStreamResult);
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::CreateStreamOnHGlobalFailure, hrAsStr);
}
if (FAILED(marshalResult)) {
AnnotateInterfaceRegistration(aIID);
nsPrintfCString hrAsStr("0x%08lX", marshalResult);
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::CoMarshalInterfaceFailure, hrAsStr);
}
if (FAILED(statResult)) {
nsPrintfCString hrAsStr("0x%08lX", statResult);
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::StatFailure,
hrAsStr);
}
if (FAILED(getHGlobalResult)) {
nsPrintfCString hrAsStr("0x%08lX", getHGlobalResult);
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::GetHGlobalFromStreamFailure, hrAsStr);
}
mStream = std::move(stream);
if (streamSize) {
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::ProxyStreamSizeFrom, "IStream::Stat"_ns);
mBufSize = streamSize;
}
if (!hglobal) {
return;
}
mGlobalLockedBuf = reinterpret_cast<BYTE*>(::GlobalLock(hglobal));
mHGlobal = hglobal;
// If we couldn't get the stream size directly from mStream, we may use
// the size of the memory block allocated by the HGLOBAL, though it might
// be larger than the actual stream size.
if (!streamSize) {
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::ProxyStreamSizeFrom, "GlobalSize"_ns);
mBufSize = static_cast<int>(::GlobalSize(hglobal));
}
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProxyStreamSize,
mBufSize);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,88 +0,0 @@
/* -*- 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/TypedEnumBits.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace mscom {
enum class ProxyStreamFlags : uint32_t {
eDefault = 0,
// When ePreservable is set on a ProxyStream, its caller *must* call
// GetPreservableStream() before the ProxyStream is destroyed.
ePreservable = 1
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ProxyStreamFlags);
class ProxyStream final {
public:
class MOZ_RAII Environment {
public:
virtual ~Environment() = default;
virtual bool Push() = 0;
virtual bool Pop() = 0;
};
class MOZ_RAII DefaultEnvironment : public Environment {
public:
bool Push() override { return true; }
bool Pop() override { return true; }
};
ProxyStream();
ProxyStream(REFIID aIID, IUnknown* aObject, Environment* aEnv,
ProxyStreamFlags aFlags = ProxyStreamFlags::eDefault);
ProxyStream(REFIID aIID, const BYTE* aInitBuf, const int aInitBufSize,
Environment* aEnv);
~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 { return !(mUnmarshaledProxy && mStream); }
bool GetInterface(void** aOutInterface);
const BYTE* GetBuffer(int& aReturnedBufSize) const;
PreservedStreamPtr GetPreservedStream();
bool operator==(const ProxyStream& aOther) const { return this == &aOther; }
private:
RefPtr<IStream> mStream;
BYTE* mGlobalLockedBuf;
HGLOBAL mHGlobal;
int mBufSize;
ProxyUniquePtr<IUnknown> mUnmarshaledProxy;
bool mPreserveStream;
};
namespace detail {
template <typename Interface>
struct EnvironmentSelector {
typedef ProxyStream::DefaultEnvironment Type;
};
} // namespace detail
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_ProxyStream_h

View File

@ -1,306 +0,0 @@
/* -*- 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/SchedulerGroup.h"
#include "mozilla/UniquePtr.h"
#include "nsError.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include <objidl.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 = SchedulerGroup::Dispatch(
TaskCategory::Other,
NewNonOwningRunnableMethod("mscom::MainThreadRelease", aPtr,
&T::Release));
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
};
template <typename T>
struct MTADelete {
void operator()(T* aPtr) {
if (!aPtr) {
return;
}
EnsureMTA::AsyncOperation([aPtr]() -> void { delete aPtr; });
}
};
template <typename T>
struct MTARelease {
void operator()(T* aPtr) {
if (!aPtr) {
return;
}
// Static analysis doesn't recognize that, even though aPtr escapes the
// current scope, we are in effect moving our strong ref into the lambda.
void* ptr = aPtr;
EnsureMTA::AsyncOperation(
[ptr]() -> void { reinterpret_cast<T*>(ptr)->Release(); });
}
};
template <typename T>
struct MTAReleaseInChildProcess {
void operator()(T* aPtr) {
if (!aPtr) {
return;
}
if (XRE_IsParentProcess()) {
MOZ_ASSERT(NS_IsMainThread());
aPtr->Release();
return;
}
// Static analysis doesn't recognize that, even though aPtr escapes the
// current scope, we are in effect moving our strong ref into the lambda.
void* ptr = aPtr;
EnsureMTA::AsyncOperation(
[ptr]() -> void { reinterpret_cast<T*>(ptr)->Release(); });
}
};
struct InterceptorTargetDeleter {
void operator()(IUnknown* aPtr) {
// We intentionally do not touch the refcounts of interceptor targets!
}
};
struct PreservedStreamDeleter {
void operator()(IStream* aPtr) {
if (!aPtr) {
return;
}
// Static analysis doesn't recognize that, even though aPtr escapes the
// current scope, we are in effect moving our strong ref into the lambda.
void* ptr = aPtr;
auto cleanup = [ptr]() -> void {
DebugOnly<HRESULT> hr =
::CoReleaseMarshalData(reinterpret_cast<LPSTREAM>(ptr));
MOZ_ASSERT(SUCCEEDED(hr));
reinterpret_cast<LPSTREAM>(ptr)->Release();
};
if (XRE_IsParentProcess()) {
MOZ_ASSERT(NS_IsMainThread());
cleanup();
return;
}
EnsureMTA::AsyncOperation(cleanup);
}
};
} // 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 MTADeletePtr = mozilla::UniquePtr<T, detail::MTADelete<T>>;
template <typename T>
using ProxyUniquePtr =
mozilla::UniquePtr<T, detail::MTAReleaseInChildProcess<T>>;
template <typename T>
using InterceptorTargetPtr =
mozilla::UniquePtr<T, detail::InterceptorTargetDeleter>;
using PreservedStreamPtr =
mozilla::UniquePtr<IStream, detail::PreservedStreamDeleter>;
namespace detail {
// We don't have direct access to UniquePtr's storage, so we use mPtrStorage
// to receive the pointer and then set the target inside the destructor.
template <typename T, typename Deleter>
class UniquePtrGetterAddRefs {
public:
explicit UniquePtrGetterAddRefs(UniquePtr<T, Deleter>& aSmartPtr)
: mTargetSmartPtr(aSmartPtr), mPtrStorage(nullptr) {}
~UniquePtrGetterAddRefs() { mTargetSmartPtr.reset(mPtrStorage); }
operator void**() { return reinterpret_cast<void**>(&mPtrStorage); }
operator T**() { return &mPtrStorage; }
T*& operator*() { return mPtrStorage; }
private:
UniquePtr<T, Deleter>& mTargetSmartPtr;
T* mPtrStorage;
};
} // namespace detail
template <typename T>
inline STAUniquePtr<T> ToSTAUniquePtr(RefPtr<T>&& aRefPtr) {
return STAUniquePtr<T>(aRefPtr.forget().take());
}
template <typename T>
inline STAUniquePtr<T> ToSTAUniquePtr(const RefPtr<T>& aRefPtr) {
MOZ_ASSERT(NS_IsMainThread());
return STAUniquePtr<T>(do_AddRef(aRefPtr).take());
}
template <typename T>
inline STAUniquePtr<T> ToSTAUniquePtr(T* aRawPtr) {
MOZ_ASSERT(NS_IsMainThread());
if (aRawPtr) {
aRawPtr->AddRef();
}
return STAUniquePtr<T>(aRawPtr);
}
template <typename T, typename U>
inline STAUniquePtr<T> ToSTAUniquePtr(const InterceptorTargetPtr<U>& aTarget) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<T> newRef(static_cast<T*>(aTarget.get()));
return ToSTAUniquePtr(std::move(newRef));
}
template <typename T>
inline MTAUniquePtr<T> ToMTAUniquePtr(RefPtr<T>&& aRefPtr) {
return MTAUniquePtr<T>(aRefPtr.forget().take());
}
template <typename T>
inline MTAUniquePtr<T> ToMTAUniquePtr(const RefPtr<T>& aRefPtr) {
MOZ_ASSERT(IsCurrentThreadMTA());
return MTAUniquePtr<T>(do_AddRef(aRefPtr).take());
}
template <typename T>
inline MTAUniquePtr<T> ToMTAUniquePtr(T* aRawPtr) {
MOZ_ASSERT(IsCurrentThreadMTA());
if (aRawPtr) {
aRawPtr->AddRef();
}
return MTAUniquePtr<T>(aRawPtr);
}
template <typename T>
inline ProxyUniquePtr<T> ToProxyUniquePtr(RefPtr<T>&& aRefPtr) {
return ProxyUniquePtr<T>(aRefPtr.forget().take());
}
template <typename T>
inline ProxyUniquePtr<T> ToProxyUniquePtr(const RefPtr<T>& aRefPtr) {
MOZ_ASSERT(IsProxy(aRefPtr));
MOZ_ASSERT((XRE_IsParentProcess() && NS_IsMainThread()) ||
(XRE_IsContentProcess() && IsCurrentThreadMTA()));
return ProxyUniquePtr<T>(do_AddRef(aRefPtr).take());
}
template <typename T>
inline ProxyUniquePtr<T> ToProxyUniquePtr(T* aRawPtr) {
MOZ_ASSERT(IsProxy(aRawPtr));
MOZ_ASSERT((XRE_IsParentProcess() && NS_IsMainThread()) ||
(XRE_IsContentProcess() && IsCurrentThreadMTA()));
if (aRawPtr) {
aRawPtr->AddRef();
}
return ProxyUniquePtr<T>(aRawPtr);
}
template <typename T, typename Deleter>
inline InterceptorTargetPtr<T> ToInterceptorTargetPtr(
const UniquePtr<T, Deleter>& aTargetPtr) {
return InterceptorTargetPtr<T>(aTargetPtr.get());
}
inline PreservedStreamPtr ToPreservedStreamPtr(RefPtr<IStream>&& aStream) {
return PreservedStreamPtr(aStream.forget().take());
}
inline PreservedStreamPtr ToPreservedStreamPtr(
already_AddRefed<IStream>& aStream) {
return PreservedStreamPtr(aStream.take());
}
template <typename T, typename Deleter>
inline detail::UniquePtrGetterAddRefs<T, Deleter> getter_AddRefs(
UniquePtr<T, Deleter>& aSmartPtr) {
return detail::UniquePtrGetterAddRefs<T, Deleter>(aSmartPtr);
}
} // namespace mscom
} // namespace mozilla
// This block makes it possible for these smart pointers to be correctly
// applied in NewRunnableMethod and friends
namespace detail {
template <typename T>
struct SmartPointerStorageClass<mozilla::mscom::STAUniquePtr<T>> {
typedef StoreCopyPassByRRef<mozilla::mscom::STAUniquePtr<T>> Type;
};
template <typename T>
struct SmartPointerStorageClass<mozilla::mscom::MTAUniquePtr<T>> {
typedef StoreCopyPassByRRef<mozilla::mscom::MTAUniquePtr<T>> Type;
};
template <typename T>
struct SmartPointerStorageClass<mozilla::mscom::ProxyUniquePtr<T>> {
typedef StoreCopyPassByRRef<mozilla::mscom::ProxyUniquePtr<T>> Type;
};
template <typename T>
struct SmartPointerStorageClass<mozilla::mscom::InterceptorTargetPtr<T>> {
typedef StoreCopyPassByRRef<mozilla::mscom::InterceptorTargetPtr<T>> Type;
};
template <>
struct SmartPointerStorageClass<mozilla::mscom::PreservedStreamPtr> {
typedef StoreCopyPassByRRef<mozilla::mscom::PreservedStreamPtr> Type;
};
} // namespace detail
#endif // mozilla_mscom_Ptr_h

View File

@ -1,449 +0,0 @@
/* -*- 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/Registration.h"
#include <utility>
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Vector.h"
#include "mozilla/mscom/ActivationContext.h"
#include "mozilla/mscom/Utils.h"
#include "nsWindowsHelpers.h"
#if defined(MOZILLA_INTERNAL_API)
# include "mozilla/ClearOnShutdown.h"
# include "mozilla/mscom/EnsureMTA.h"
HRESULT RegisterPassthruProxy();
#else
# include <stdlib.h>
#endif // defined(MOZILLA_INTERNAL_API)
#include <oaidl.h>
#include <objidl.h>
#include <rpcproxy.h>
#include <shlwapi.h>
#include <algorithm>
/* 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! */
extern "C" {
// This function is defined in generated code for proxy DLLs but is not declared
// in rpcproxy.h, so we need this declaration.
void RPC_ENTRY GetProxyDllInfo(const ProxyFileInfo*** aInfo, const CLSID** aId);
}
namespace mozilla {
namespace mscom {
static bool GetContainingLibPath(wchar_t* aBuffer, size_t aBufferLen) {
HMODULE thisModule = reinterpret_cast<HMODULE>(GetContainingModuleHandle());
if (!thisModule) {
return false;
}
DWORD fileNameResult = GetModuleFileName(thisModule, aBuffer, aBufferLen);
if (!fileNameResult || (fileNameResult == aBufferLen &&
::GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
return false;
}
return true;
}
static bool BuildLibPath(RegistrationFlags aFlags, wchar_t* aBuffer,
size_t aBufferLen, const wchar_t* aLeafName) {
if (aFlags == RegistrationFlags::eUseBinDirectory) {
if (!GetContainingLibPath(aBuffer, aBufferLen)) {
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;
}
static bool RegisterPSClsids(const ProxyFileInfo** aProxyInfo,
const CLSID* aProxyClsid) {
while (*aProxyInfo) {
const ProxyFileInfo& curInfo = **aProxyInfo;
for (unsigned short idx = 0, size = curInfo.TableSize; idx < size; ++idx) {
HRESULT hr = CoRegisterPSClsid(*(curInfo.pStubVtblList[idx]->header.piid),
*aProxyClsid);
if (FAILED(hr)) {
return false;
}
}
++aProxyInfo;
}
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.
ActivationContextRegion actCtxRgn(proxyDll.get());
auto GetProxyDllInfoFn = reinterpret_cast<decltype(&GetProxyDllInfo)>(
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));
if (!RegisterPSClsids(proxyInfo, proxyClsid)) {
return nullptr;
}
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)
#if defined(MOZILLA_INTERNAL_API)
,
mIsRegisteredInMTA(IsCurrentThreadMTA())
#endif // defined(MOZILLA_INTERNAL_API)
{
MOZ_ASSERT(aClassObject);
MOZ_ASSERT(aTypeLib);
AddToRegistry(this);
}
RegisteredProxy::RegisteredProxy(IUnknown* aClassObject, uint32_t aRegCookie,
ITypeLib* aTypeLib)
: mModule(0),
mClassObject(aClassObject),
mRegCookie(aRegCookie),
mTypeLib(aTypeLib)
#if defined(MOZILLA_INTERNAL_API)
,
mIsRegisteredInMTA(IsCurrentThreadMTA())
#endif // defined(MOZILLA_INTERNAL_API)
{
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)
#if defined(MOZILLA_INTERNAL_API)
,
mIsRegisteredInMTA(false)
#endif // defined(MOZILLA_INTERNAL_API)
{
MOZ_ASSERT(aTypeLib);
AddToRegistry(this);
}
void RegisteredProxy::Clear() {
if (mTypeLib) {
mTypeLib->lpVtbl->Release(mTypeLib);
mTypeLib = nullptr;
}
if (mClassObject) {
// NB: mClassObject and mRegCookie must be freed from inside the apartment
// which they were created in.
auto cleanupFn = [&]() -> void {
::CoRevokeClassObject(mRegCookie);
mRegCookie = 0;
mClassObject->lpVtbl->Release(mClassObject);
mClassObject = nullptr;
};
#if defined(MOZILLA_INTERNAL_API)
// This code only supports MTA when built internally
if (mIsRegisteredInMTA) {
EnsureMTA mta(cleanupFn);
} else {
cleanupFn();
}
#else
cleanupFn();
#endif // defined(MOZILLA_INTERNAL_API)
}
if (mModule) {
::FreeLibrary(reinterpret_cast<HMODULE>(mModule));
mModule = 0;
}
}
RegisteredProxy::~RegisteredProxy() {
DeleteFromRegistry(this);
Clear();
}
RegisteredProxy::RegisteredProxy(RegisteredProxy&& aOther)
: mModule(0),
mClassObject(nullptr),
mRegCookie(0),
mTypeLib(nullptr)
#if defined(MOZILLA_INTERNAL_API)
,
mIsRegisteredInMTA(false)
#endif // defined(MOZILLA_INTERNAL_API)
{
*this = std::forward<RegisteredProxy>(aOther);
AddToRegistry(this);
}
RegisteredProxy& RegisteredProxy::operator=(RegisteredProxy&& aOther) {
Clear();
mModule = aOther.mModule;
aOther.mModule = 0;
mClassObject = aOther.mClassObject;
aOther.mClassObject = nullptr;
mRegCookie = aOther.mRegCookie;
aOther.mRegCookie = 0;
mTypeLib = aOther.mTypeLib;
aOther.mTypeLib = nullptr;
#if defined(MOZILLA_INTERNAL_API)
mIsRegisteredInMTA = aOther.mIsRegisteredInMTA;
#endif // defined(MOZILLA_INTERNAL_API)
return *this;
}
HRESULT
RegisteredProxy::GetTypeInfoForGuid(REFGUID aGuid,
ITypeInfo** aOutTypeInfo) const {
if (!aOutTypeInfo) {
return E_INVALIDARG;
}
if (!mTypeLib) {
return E_UNEXPECTED;
}
return mTypeLib->lpVtbl->GetTypeInfoOfGuid(mTypeLib, aGuid, aOutTypeInfo);
}
static StaticAutoPtr<Vector<RegisteredProxy*>> sRegistry;
namespace UseGetMutexForAccess {
// This must not be accessed directly; use GetMutex() instead
static CRITICAL_SECTION sMutex;
} // namespace UseGetMutexForAccess
static CRITICAL_SECTION* GetMutex() {
static CRITICAL_SECTION& mutex = []() -> CRITICAL_SECTION& {
#if defined(RELEASE_OR_BETA)
DWORD flags = CRITICAL_SECTION_NO_DEBUG_INFO;
#else
DWORD flags = 0;
#endif
InitializeCriticalSectionEx(&UseGetMutexForAccess::sMutex, 4000, flags);
#if !defined(MOZILLA_INTERNAL_API)
atexit([]() { DeleteCriticalSection(&UseGetMutexForAccess::sMutex); });
#endif
return UseGetMutexForAccess::sMutex;
}();
return &mutex;
}
/* static */
bool RegisteredProxy::Find(REFIID aIid, ITypeInfo** aTypeInfo) {
AutoCriticalSection lock(GetMutex());
if (!sRegistry) {
return false;
}
for (auto&& proxy : *sRegistry) {
if (SUCCEEDED(proxy->GetTypeInfoForGuid(aIid, aTypeInfo))) {
return true;
}
}
return false;
}
/* static */
void RegisteredProxy::AddToRegistry(RegisteredProxy* aProxy) {
MOZ_ASSERT(aProxy);
AutoCriticalSection lock(GetMutex());
if (!sRegistry) {
sRegistry = new Vector<RegisteredProxy*>();
#if !defined(MOZILLA_INTERNAL_API)
// sRegistry allocation is fallible outside of Mozilla processes
if (!sRegistry) {
return;
}
#endif
}
MOZ_ALWAYS_TRUE(sRegistry->emplaceBack(aProxy));
}
/* static */
void RegisteredProxy::DeleteFromRegistry(RegisteredProxy* aProxy) {
MOZ_ASSERT(aProxy);
AutoCriticalSection lock(GetMutex());
MOZ_ASSERT(sRegistry && !sRegistry->empty());
if (!sRegistry) {
return;
}
sRegistry->erase(std::remove(sRegistry->begin(), sRegistry->end(), aProxy),
sRegistry->end());
if (sRegistry->empty()) {
sRegistry = nullptr;
}
}
#if defined(MOZILLA_INTERNAL_API)
static StaticAutoPtr<Vector<std::pair<const ArrayData*, size_t>>> sArrayData;
void RegisterArrayData(const ArrayData* aArrayData, size_t aLength) {
AutoCriticalSection lock(GetMutex());
if (!sArrayData) {
sArrayData = new Vector<std::pair<const ArrayData*, size_t>>();
ClearOnShutdown(&sArrayData, ShutdownPhase::XPCOMShutdownThreads);
}
MOZ_ALWAYS_TRUE(sArrayData->emplaceBack(std::make_pair(aArrayData, aLength)));
}
const ArrayData* FindArrayData(REFIID aIid, ULONG aMethodIndex) {
AutoCriticalSection lock(GetMutex());
if (!sArrayData) {
return nullptr;
}
for (auto&& data : *sArrayData) {
for (size_t innerIdx = 0, innerLen = data.second; innerIdx < innerLen;
++innerIdx) {
const ArrayData* array = data.first;
if (aMethodIndex == array[innerIdx].mMethodIndex &&
IsInterfaceEqualToOrInheritedFrom(aIid, array[innerIdx].mIid,
aMethodIndex)) {
return &array[innerIdx];
}
}
}
return nullptr;
}
#endif // defined(MOZILLA_INTERNAL_API)
} // namespace mscom
} // namespace mozilla

View File

@ -1,138 +0,0 @@
/* -*- 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);
RegisteredProxy(IUnknown* aClassObject, uint32_t aRegCookie,
ITypeLib* aTypeLib);
explicit RegisteredProxy(ITypeLib* aTypeLib);
RegisteredProxy(RegisteredProxy&& aOther);
RegisteredProxy& operator=(RegisteredProxy&& aOther);
~RegisteredProxy();
HRESULT GetTypeInfoForGuid(REFGUID aGuid, ITypeInfo** aOutTypeInfo) const;
static bool Find(REFIID aIid, ITypeInfo** aOutTypeInfo);
private:
RegisteredProxy() = delete;
RegisteredProxy(RegisteredProxy&) = delete;
RegisteredProxy& operator=(RegisteredProxy&) = delete;
void Clear();
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;
#if defined(MOZILLA_INTERNAL_API)
bool mIsRegisteredInMTA;
#endif // defined(MOZILLA_INTERNAL_API)
};
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);
#if defined(MOZILLA_INTERNAL_API)
/**
* 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);
#endif // defined(MOZILLA_INTERNAL_API)
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Registration_h

View File

@ -1,386 +0,0 @@
/* -*- 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 "RegistrationAnnotator.h"
#include "mozilla/JSONStringWriteFuncs.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/NotNull.h"
#include "mozilla/RefPtr.h"
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"
#include "nsWindowsHelpers.h"
#include "nsXULAppAPI.h"
#include <oleauto.h>
namespace mozilla {
namespace mscom {
static const char16_t kSoftwareClasses[] = u"SOFTWARE\\Classes";
static const char16_t kInterface[] = u"\\Interface\\";
static const char16_t kDefaultValue[] = u"";
static const char16_t kThreadingModel[] = u"ThreadingModel";
static const char16_t kBackslash[] = u"\\";
static const char16_t kFlags[] = u"FLAGS";
static const char16_t kProxyStubClsid32[] = u"\\ProxyStubClsid32";
static const char16_t kClsid[] = u"\\CLSID\\";
static const char16_t kInprocServer32[] = u"\\InprocServer32";
static const char16_t kInprocHandler32[] = u"\\InprocHandler32";
static const char16_t kTypeLib[] = u"\\TypeLib";
static const char16_t kVersion[] = u"Version";
static const char16_t kWin32[] = u"Win32";
static const char16_t kWin64[] = u"Win64";
static bool GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
const nsAString& aValueName, nsAString& aOutput) {
const nsString& flatSubKey = PromiseFlatString(aStrSubKey);
const nsString& flatValueName = PromiseFlatString(aValueName);
LPCWSTR valueName = aValueName.IsEmpty() ? nullptr : flatValueName.get();
DWORD type = 0;
DWORD numBytes = 0;
LONG result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, RRF_RT_ANY,
&type, nullptr, &numBytes);
if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
return false;
}
int numChars = (numBytes + 1) / sizeof(wchar_t);
aOutput.SetLength(numChars);
DWORD acceptFlag = type == REG_SZ ? RRF_RT_REG_SZ : RRF_RT_REG_EXPAND_SZ;
result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, acceptFlag,
nullptr, aOutput.BeginWriting(), &numBytes);
if (result == ERROR_SUCCESS) {
// Truncate null terminator
aOutput.SetLength(((numBytes + 1) / sizeof(wchar_t)) - 1);
}
return result == ERROR_SUCCESS;
}
template <size_t N>
inline static bool GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
const char16_t (&aValueName)[N],
nsAString& aOutput) {
return GetStringValue(aBaseKey, aStrSubKey, nsLiteralString(aValueName),
aOutput);
}
/**
* This function fails unless the entire string has been converted.
* (eg, the string "FLAGS" will convert to 0xF but we will return false)
*/
static bool ConvertLCID(const wchar_t* aStr, NotNull<unsigned long*> aOutLcid) {
wchar_t* endChar;
*aOutLcid = wcstoul(aStr, &endChar, 16);
return *endChar == 0;
}
static bool GetLoadedPath(nsAString& aPath) {
// These paths may be REG_EXPAND_SZ, so we expand any environment strings
DWORD bufCharLen =
ExpandEnvironmentStrings(PromiseFlatString(aPath).get(), nullptr, 0);
auto buf = MakeUnique<WCHAR[]>(bufCharLen);
if (!ExpandEnvironmentStrings(PromiseFlatString(aPath).get(), buf.get(),
bufCharLen)) {
return false;
}
// Use LoadLibrary so that the DLL is resolved using the loader's DLL search
// rules
nsModuleHandle mod(LoadLibrary(buf.get()));
if (!mod) {
return false;
}
WCHAR finalPath[MAX_PATH + 1] = {};
DWORD result = GetModuleFileNameW(mod, finalPath, ArrayLength(finalPath));
if (!result || (result == ArrayLength(finalPath) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
return false;
}
aPath = nsDependentString(finalPath, result);
return true;
}
static void AnnotateClsidRegistrationForHive(
JSONWriter& aJson, HKEY aHive, const nsAString& aClsid,
const JSONWriter::CollectionStyle aStyle) {
nsAutoString clsidSubkey;
clsidSubkey.AppendLiteral(kSoftwareClasses);
clsidSubkey.AppendLiteral(kClsid);
clsidSubkey.Append(aClsid);
nsAutoString className;
if (GetStringValue(aHive, clsidSubkey, kDefaultValue, className)) {
aJson.StringProperty("ClassName", NS_ConvertUTF16toUTF8(className));
}
nsAutoString inprocServerSubkey(clsidSubkey);
inprocServerSubkey.AppendLiteral(kInprocServer32);
nsAutoString pathToServerDll;
if (GetStringValue(aHive, inprocServerSubkey, kDefaultValue,
pathToServerDll)) {
aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(pathToServerDll));
if (GetLoadedPath(pathToServerDll)) {
aJson.StringProperty("LoadedPath",
NS_ConvertUTF16toUTF8(pathToServerDll));
}
}
nsAutoString apartment;
if (GetStringValue(aHive, inprocServerSubkey, kThreadingModel, apartment)) {
aJson.StringProperty("ThreadingModel", NS_ConvertUTF16toUTF8(apartment));
}
nsAutoString inprocHandlerSubkey(clsidSubkey);
inprocHandlerSubkey.AppendLiteral(kInprocHandler32);
nsAutoString pathToHandlerDll;
if (GetStringValue(aHive, inprocHandlerSubkey, kDefaultValue,
pathToHandlerDll)) {
aJson.StringProperty("HandlerPath",
NS_ConvertUTF16toUTF8(pathToHandlerDll));
if (GetLoadedPath(pathToHandlerDll)) {
aJson.StringProperty("LoadedHandlerPath",
NS_ConvertUTF16toUTF8(pathToHandlerDll));
}
}
nsAutoString handlerApartment;
if (GetStringValue(aHive, inprocHandlerSubkey, kThreadingModel,
handlerApartment)) {
aJson.StringProperty("HandlerThreadingModel",
NS_ConvertUTF16toUTF8(handlerApartment));
}
}
static void CheckTlbPath(JSONWriter& aJson, const nsAString& aTypelibPath) {
const nsString& flatPath = PromiseFlatString(aTypelibPath);
DWORD bufCharLen = ExpandEnvironmentStrings(flatPath.get(), nullptr, 0);
auto buf = MakeUnique<WCHAR[]>(bufCharLen);
if (!ExpandEnvironmentStrings(flatPath.get(), buf.get(), bufCharLen)) {
return;
}
// See whether this tlb can actually be loaded
RefPtr<ITypeLib> typeLib;
HRESULT hr = LoadTypeLibEx(buf.get(), REGKIND_NONE, getter_AddRefs(typeLib));
nsPrintfCString loadResult("0x%08lX", hr);
aJson.StringProperty("LoadResult", loadResult);
}
template <size_t N>
static void AnnotateTypelibPlatform(JSONWriter& aJson, HKEY aBaseKey,
const nsAString& aLcidSubkey,
const char16_t (&aPlatform)[N],
const JSONWriter::CollectionStyle aStyle) {
nsLiteralString platform(aPlatform);
nsAutoString fullSubkey(aLcidSubkey);
fullSubkey.AppendLiteral(kBackslash);
fullSubkey.Append(platform);
nsAutoString tlbPath;
if (GetStringValue(aBaseKey, fullSubkey, kDefaultValue, tlbPath)) {
aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(platform), aStyle);
aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(tlbPath));
CheckTlbPath(aJson, tlbPath);
aJson.EndObject();
}
}
static void AnnotateTypelibRegistrationForHive(
JSONWriter& aJson, HKEY aHive, const nsAString& aTypelibId,
const nsAString& aTypelibVersion,
const JSONWriter::CollectionStyle aStyle) {
nsAutoString typelibSubKey;
typelibSubKey.AppendLiteral(kSoftwareClasses);
typelibSubKey.AppendLiteral(kTypeLib);
typelibSubKey.AppendLiteral(kBackslash);
typelibSubKey.Append(aTypelibId);
typelibSubKey.AppendLiteral(kBackslash);
typelibSubKey.Append(aTypelibVersion);
nsAutoString typelibDesc;
if (GetStringValue(aHive, typelibSubKey, kDefaultValue, typelibDesc)) {
aJson.StringProperty("Description", NS_ConvertUTF16toUTF8(typelibDesc));
}
nsAutoString flagsSubKey(typelibSubKey);
flagsSubKey.AppendLiteral(kBackslash);
flagsSubKey.AppendLiteral(kFlags);
nsAutoString typelibFlags;
if (GetStringValue(aHive, flagsSubKey, kDefaultValue, typelibFlags)) {
aJson.StringProperty("Flags", NS_ConvertUTF16toUTF8(typelibFlags));
}
HKEY rawTypelibKey;
LONG result =
RegOpenKeyEx(aHive, typelibSubKey.get(), 0, KEY_READ, &rawTypelibKey);
if (result != ERROR_SUCCESS) {
return;
}
nsAutoRegKey typelibKey(rawTypelibKey);
const size_t kMaxLcidCharLen = 9;
WCHAR keyName[kMaxLcidCharLen];
for (DWORD index = 0; result == ERROR_SUCCESS; ++index) {
DWORD keyNameLength = ArrayLength(keyName);
result = RegEnumKeyEx(typelibKey, index, keyName, &keyNameLength, nullptr,
nullptr, nullptr, nullptr);
unsigned long lcid;
if (result == ERROR_SUCCESS && ConvertLCID(keyName, WrapNotNull(&lcid))) {
nsDependentString strLcid(keyName, keyNameLength);
aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(strLcid), aStyle);
AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin32, aStyle);
#if defined(HAVE_64BIT_BUILD)
AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin64, aStyle);
#endif
aJson.EndObject();
}
}
}
static void AnnotateInterfaceRegistrationForHive(
JSONWriter& aJson, HKEY aHive, REFIID aIid,
const JSONWriter::CollectionStyle aStyle) {
nsAutoString interfaceSubKey;
interfaceSubKey.AppendLiteral(kSoftwareClasses);
interfaceSubKey.AppendLiteral(kInterface);
nsAutoString iid;
GUIDToString(aIid, iid);
interfaceSubKey.Append(iid);
nsAutoString interfaceName;
if (GetStringValue(aHive, interfaceSubKey, kDefaultValue, interfaceName)) {
aJson.StringProperty("InterfaceName", NS_ConvertUTF16toUTF8(interfaceName));
}
nsAutoString psSubKey(interfaceSubKey);
psSubKey.AppendLiteral(kProxyStubClsid32);
nsAutoString psClsid;
if (GetStringValue(aHive, psSubKey, kDefaultValue, psClsid)) {
aJson.StartObjectProperty("ProxyStub", aStyle);
aJson.StringProperty("CLSID", NS_ConvertUTF16toUTF8(psClsid));
AnnotateClsidRegistrationForHive(aJson, aHive, psClsid, aStyle);
aJson.EndObject();
}
nsAutoString typelibSubKey(interfaceSubKey);
typelibSubKey.AppendLiteral(kTypeLib);
nsAutoString typelibId;
bool haveTypelibId =
GetStringValue(aHive, typelibSubKey, kDefaultValue, typelibId);
nsAutoString typelibVersion;
bool haveTypelibVersion =
GetStringValue(aHive, typelibSubKey, kVersion, typelibVersion);
if (haveTypelibId || haveTypelibVersion) {
aJson.StartObjectProperty("TypeLib", aStyle);
}
if (haveTypelibId) {
aJson.StringProperty("ID", NS_ConvertUTF16toUTF8(typelibId));
}
if (haveTypelibVersion) {
aJson.StringProperty("Version", NS_ConvertUTF16toUTF8(typelibVersion));
}
if (haveTypelibId && haveTypelibVersion) {
AnnotateTypelibRegistrationForHive(aJson, aHive, typelibId, typelibVersion,
aStyle);
}
if (haveTypelibId || haveTypelibVersion) {
aJson.EndObject();
}
}
void AnnotateInterfaceRegistration(REFIID aIid) {
#if defined(DEBUG)
const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
#else
const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
#endif
JSONStringWriteFunc<nsCString> jsonString;
JSONWriter json(jsonString);
json.Start(style);
json.StartObjectProperty("HKLM", style);
AnnotateInterfaceRegistrationForHive(json, HKEY_LOCAL_MACHINE, aIid, style);
json.EndObject();
json.StartObjectProperty("HKCU", style);
AnnotateInterfaceRegistrationForHive(json, HKEY_CURRENT_USER, aIid, style);
json.EndObject();
json.End();
CrashReporter::Annotation annotationKey;
if (XRE_IsParentProcess()) {
annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoParent;
} else {
annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoChild;
}
CrashReporter::AnnotateCrashReport(annotationKey, jsonString.StringCRef());
}
void AnnotateClassRegistration(REFCLSID aClsid) {
#if defined(DEBUG)
const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
#else
const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
#endif
nsAutoString strClsid;
GUIDToString(aClsid, strClsid);
JSONStringWriteFunc<nsCString> jsonString;
JSONWriter json(jsonString);
json.Start(style);
json.StartObjectProperty("HKLM", style);
AnnotateClsidRegistrationForHive(json, HKEY_LOCAL_MACHINE, strClsid, style);
json.EndObject();
json.StartObjectProperty("HKCU", style);
AnnotateClsidRegistrationForHive(json, HKEY_CURRENT_USER, strClsid, style);
json.EndObject();
json.End();
CrashReporter::Annotation annotationKey;
if (XRE_IsParentProcess()) {
annotationKey = CrashReporter::Annotation::ClassRegistrationInfoParent;
} else {
annotationKey = CrashReporter::Annotation::ClassRegistrationInfoChild;
}
CrashReporter::AnnotateCrashReport(annotationKey, jsonString.StringCRef());
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,21 +0,0 @@
/* -*- 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_RegistrationAnnotator_h
#define mozilla_mscom_RegistrationAnnotator_h
#include <windows.h>
namespace mozilla {
namespace mscom {
void AnnotateInterfaceRegistration(REFIID aIid);
void AnnotateClassRegistration(REFCLSID aClsid);
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_RegistrationAnnotator_h

View File

@ -1,77 +0,0 @@
/* -*- 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/SpinEvent.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/TimeStamp.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsSystemInfo.h"
namespace mozilla {
namespace mscom {
static const TimeDuration kMaxSpinTime = TimeDuration::FromMilliseconds(30);
bool SpinEvent::sIsMulticore = false;
/* static */
bool SpinEvent::InitStatics() {
SYSTEM_INFO sysInfo;
::GetSystemInfo(&sysInfo);
sIsMulticore = sysInfo.dwNumberOfProcessors > 1;
return true;
}
SpinEvent::SpinEvent() : mDone(false) {
static const bool gotStatics = InitStatics();
MOZ_ALWAYS_TRUE(gotStatics);
mDoneEvent.own(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
MOZ_ASSERT(mDoneEvent);
}
bool SpinEvent::Wait(HANDLE aTargetThread) {
MOZ_ASSERT(aTargetThread);
if (!aTargetThread) {
return false;
}
if (sIsMulticore) {
// Bug 1311834: Spinning allows for faster response than waiting on an
// event, as events are constrained by the system's timer resolution.
// Bug 1429665: However, we only want to spin for a very short time. If
// we're waiting for a while, we don't want to be burning CPU for the
// entire time. At that point, a few extra ms isn't going to make much
// difference to perceived responsiveness.
TimeStamp start(TimeStamp::Now());
while (!mDone) {
TimeDuration elapsed(TimeStamp::Now() - start);
if (elapsed >= kMaxSpinTime) {
break;
}
YieldProcessor();
}
if (mDone) {
return true;
}
}
MOZ_ASSERT(mDoneEvent);
HANDLE handles[] = {mDoneEvent, aTargetThread};
DWORD waitResult = ::WaitForMultipleObjects(mozilla::ArrayLength(handles),
handles, FALSE, INFINITE);
return waitResult == WAIT_OBJECT_0;
}
void SpinEvent::Signal() {
::SetEvent(mDoneEvent);
mDone = true;
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,40 +0,0 @@
/* -*- 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_SpinEvent_h
#define mozilla_mscom_SpinEvent_h
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "nsWindowsHelpers.h"
namespace mozilla {
namespace mscom {
class MOZ_NON_TEMPORARY_CLASS SpinEvent final {
public:
SpinEvent();
~SpinEvent() = default;
bool Wait(HANDLE aTargetThread);
void Signal();
SpinEvent(const SpinEvent&) = delete;
SpinEvent(SpinEvent&&) = delete;
SpinEvent& operator=(SpinEvent&&) = delete;
SpinEvent& operator=(const SpinEvent&) = delete;
private:
Atomic<bool, ReleaseAcquire> mDone;
nsAutoHandle mDoneEvent;
static bool InitStatics();
static bool sIsMulticore;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_SpinEvent_h

View File

@ -1,23 +0,0 @@
/* -*- 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 <malloc.h>
#include <rpc.h>
/**
* These functions need to be defined in order for the types that use
* mozilla::mscom::StructToStream and mozilla::mscom::StructFromStream to work.
*/
extern "C" {
void __RPC_FAR* __RPC_USER midl_user_allocate(size_t aNumBytes) {
const unsigned long kRpcReqdBufAlignment = 8;
return _aligned_malloc(aNumBytes, kRpcReqdBufAlignment);
}
void __RPC_USER midl_user_free(void* aBuffer) { _aligned_free(aBuffer); }
} // extern "C"

View File

@ -1,239 +0,0 @@
/* -*- 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_StructStream_h
#define mozilla_mscom_StructStream_h
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "nscore.h"
#include <memory.h>
#include <midles.h>
#include <objidl.h>
#include <rpc.h>
/**
* This code is used for (de)serializing data structures that have been
* declared using midl, thus allowing us to use Microsoft RPC for marshaling
* data for our COM handlers that may run in other processes that are not ours.
*/
namespace mozilla {
namespace mscom {
namespace detail {
typedef ULONG EncodedLenT;
} // namespace detail
class MOZ_NON_TEMPORARY_CLASS StructToStream {
public:
/**
* This constructor variant represents an empty/null struct to be serialized.
*/
StructToStream()
: mStatus(RPC_S_OK), mHandle(nullptr), mBuffer(nullptr), mEncodedLen(0) {}
template <typename StructT>
StructToStream(StructT& aSrcStruct, void (*aEncodeFnPtr)(handle_t, StructT*))
: mStatus(RPC_X_INVALID_BUFFER),
mHandle(nullptr),
mBuffer(nullptr),
mEncodedLen(0) {
mStatus =
::MesEncodeDynBufferHandleCreate(&mBuffer, &mEncodedLen, &mHandle);
if (mStatus != RPC_S_OK) {
return;
}
MOZ_SEH_TRY { aEncodeFnPtr(mHandle, &aSrcStruct); }
#ifdef HAVE_SEH_EXCEPTIONS
MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
mStatus = ::RpcExceptionCode();
return;
}
#endif
if (!mBuffer || !mEncodedLen) {
mStatus = RPC_X_NO_MEMORY;
return;
}
}
~StructToStream() {
if (mHandle) {
::MesHandleFree(mHandle);
}
if (mBuffer) {
// Bug 1440564: You'd think that MesHandleFree would free the buffer,
// since it was created by RPC, but it doesn't.
midl_user_free(mBuffer);
}
}
static unsigned long GetEmptySize() { return sizeof(detail::EncodedLenT); }
static HRESULT WriteEmpty(IStream* aDestStream) {
StructToStream emptyStruct;
return emptyStruct.Write(aDestStream);
}
explicit operator bool() const { return mStatus == RPC_S_OK; }
bool IsEmpty() const { return mStatus == RPC_S_OK && !mEncodedLen; }
unsigned long GetSize() const { return sizeof(mEncodedLen) + mEncodedLen; }
HRESULT Write(IStream* aDestStream) {
if (!aDestStream) {
return E_INVALIDARG;
}
if (mStatus != RPC_S_OK) {
return E_FAIL;
}
ULONG bytesWritten;
HRESULT hr =
aDestStream->Write(&mEncodedLen, sizeof(mEncodedLen), &bytesWritten);
if (FAILED(hr)) {
return hr;
}
if (bytesWritten != sizeof(mEncodedLen)) {
return E_UNEXPECTED;
}
if (mBuffer && mEncodedLen) {
hr = aDestStream->Write(mBuffer, mEncodedLen, &bytesWritten);
if (FAILED(hr)) {
return hr;
}
if (bytesWritten != mEncodedLen) {
return E_UNEXPECTED;
}
}
return hr;
}
StructToStream(const StructToStream&) = delete;
StructToStream(StructToStream&&) = delete;
StructToStream& operator=(const StructToStream&) = delete;
StructToStream& operator=(StructToStream&&) = delete;
private:
RPC_STATUS mStatus;
handle_t mHandle;
char* mBuffer;
detail::EncodedLenT mEncodedLen;
};
class MOZ_NON_TEMPORARY_CLASS StructFromStream {
struct AlignedFreeDeleter {
void operator()(void* aPtr) { ::_aligned_free(aPtr); }
};
static const detail::EncodedLenT kRpcReqdBufAlignment = 8;
public:
explicit StructFromStream(IStream* aStream)
: mStatus(RPC_X_INVALID_BUFFER), mHandle(nullptr) {
MOZ_ASSERT(aStream);
// Read the length of the encoded data first
detail::EncodedLenT encodedLen = 0;
ULONG bytesRead = 0;
HRESULT hr = aStream->Read(&encodedLen, sizeof(encodedLen), &bytesRead);
if (FAILED(hr)) {
return;
}
// NB: Some implementations of IStream return S_FALSE to indicate EOF,
// other implementations return S_OK and set the number of bytes read to 0.
// We must handle both.
if (hr == S_FALSE || !bytesRead) {
mStatus = RPC_S_OBJECT_NOT_FOUND;
return;
}
if (bytesRead != sizeof(encodedLen)) {
return;
}
if (!encodedLen) {
mStatus = RPC_S_OBJECT_NOT_FOUND;
return;
}
MOZ_ASSERT(encodedLen % kRpcReqdBufAlignment == 0);
if (encodedLen % kRpcReqdBufAlignment) {
return;
}
// This memory allocation is fallible
mEncodedBuffer.reset(static_cast<char*>(
::_aligned_malloc(encodedLen, kRpcReqdBufAlignment)));
if (!mEncodedBuffer) {
return;
}
ULONG bytesReadFromStream = 0;
hr = aStream->Read(mEncodedBuffer.get(), encodedLen, &bytesReadFromStream);
if (FAILED(hr) || bytesReadFromStream != encodedLen) {
return;
}
mStatus = ::MesDecodeBufferHandleCreate(mEncodedBuffer.get(), encodedLen,
&mHandle);
}
~StructFromStream() {
if (mHandle) {
::MesHandleFree(mHandle);
}
}
explicit operator bool() const { return mStatus == RPC_S_OK || IsEmpty(); }
bool IsEmpty() const { return mStatus == RPC_S_OBJECT_NOT_FOUND; }
template <typename StructT>
bool Read(StructT* aDestStruct, void (*aDecodeFnPtr)(handle_t, StructT*)) {
if (!aDestStruct || !aDecodeFnPtr || mStatus != RPC_S_OK) {
return false;
}
// NB: Deserialization will fail with BSTRs unless the destination data
// is zeroed out!
ZeroMemory(aDestStruct, sizeof(StructT));
MOZ_SEH_TRY { aDecodeFnPtr(mHandle, aDestStruct); }
#ifdef HAVE_SEH_EXCEPTIONS
MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
mStatus = ::RpcExceptionCode();
return false;
}
#endif
return true;
}
StructFromStream(const StructFromStream&) = delete;
StructFromStream(StructFromStream&&) = delete;
StructFromStream& operator=(const StructFromStream&) = delete;
StructFromStream& operator=(StructFromStream&&) = delete;
private:
RPC_STATUS mStatus;
handle_t mHandle;
UniquePtr<char, AlignedFreeDeleter> mEncodedBuffer;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_StructStream_h

View File

@ -9,17 +9,9 @@
# include "mozilla/dom/ContentChild.h"
#endif
#if defined(ACCESSIBILITY)
# include "mozilla/mscom/Registration.h"
# if defined(MOZILLA_INTERNAL_API)
# include "nsTArray.h"
# endif
#endif
#include "mozilla/ArrayUtils.h"
#include "mozilla/mscom/COMWrappers.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/mscom/Objref.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/RefPtr.h"
#include "mozilla/WindowsVersion.h"
@ -267,42 +259,6 @@ long CreateStream(const uint8_t* aInitBuf, const uint32_t aInitBufSize,
return S_OK;
}
long CopySerializedProxy(IStream* aInStream, IStream** aOutStream) {
if (!aInStream || !aOutStream) {
return E_INVALIDARG;
}
*aOutStream = nullptr;
uint32_t desiredStreamSize = GetOBJREFSize(WrapNotNull(aInStream));
if (!desiredStreamSize) {
return E_INVALIDARG;
}
RefPtr<IStream> stream;
HRESULT hr = CreateStream(nullptr, desiredStreamSize, getter_AddRefs(stream));
if (FAILED(hr)) {
return hr;
}
ULARGE_INTEGER numBytesToCopy;
numBytesToCopy.QuadPart = desiredStreamSize;
hr = aInStream->CopyTo(stream, numBytesToCopy, nullptr, nullptr);
if (FAILED(hr)) {
return hr;
}
LARGE_INTEGER seekTo;
seekTo.QuadPart = 0LL;
hr = stream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
if (FAILED(hr)) {
return hr;
}
stream.forget(aOutStream);
return S_OK;
}
#if defined(MOZILLA_INTERNAL_API)
void GUIDToString(REFGUID aGuid, nsAString& aOutString) {
@ -406,158 +362,6 @@ void GUIDToString(REFGUID aGuid,
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
static bool IsVtableIndexFromParentInterface(TYPEATTR* aTypeAttr,
unsigned long aVtableIndex) {
MOZ_ASSERT(aTypeAttr);
// This is the number of functions declared in this interface (excluding
// parent interfaces).
unsigned int numExclusiveFuncs = aTypeAttr->cFuncs;
// This is the number of vtable entries (which includes parent interfaces).
// TYPEATTR::cbSizeVft is the entire vtable size in bytes, so we need to
// divide in order to compute the number of entries.
unsigned int numVtblEntries = aTypeAttr->cbSizeVft / sizeof(void*);
// This is the index of the first entry in the vtable that belongs to this
// interface and not a parent.
unsigned int firstVtblIndex = numVtblEntries - numExclusiveFuncs;
// If aVtableIndex is less than firstVtblIndex, then we're asking for an
// index that may belong to a parent interface.
return aVtableIndex < firstVtblIndex;
}
bool IsVtableIndexFromParentInterface(REFIID aInterface,
unsigned long aVtableIndex) {
RefPtr<ITypeInfo> typeInfo;
if (!RegisteredProxy::Find(aInterface, getter_AddRefs(typeInfo))) {
return false;
}
TYPEATTR* typeAttr = nullptr;
HRESULT hr = typeInfo->GetTypeAttr(&typeAttr);
if (FAILED(hr)) {
return false;
}
bool result = IsVtableIndexFromParentInterface(typeAttr, aVtableIndex);
typeInfo->ReleaseTypeAttr(typeAttr);
return result;
}
# if defined(MOZILLA_INTERNAL_API)
bool IsCallerExternalProcess() {
MOZ_ASSERT(XRE_IsContentProcess());
/**
* CoGetCallerTID() gives us the caller's thread ID when that thread resides
* in a single-threaded apartment. Since our chrome main thread does live
* inside an STA, we will therefore be able to check whether the caller TID
* equals our chrome main thread TID. This enables us to distinguish
* between our chrome thread vs other out-of-process callers. We check for
* S_FALSE to ensure that the caller is a different process from ours, which
* is the only scenario that we care about.
*/
DWORD callerTid;
if (::CoGetCallerTID(&callerTid) != S_FALSE) {
return false;
}
// Now check whether the caller is our parent process main thread.
const DWORD parentMainTid =
dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
return callerTid != parentMainTid;
}
bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
unsigned long aVtableIndexHint) {
if (aInterface == aFrom) {
return true;
}
// We expect this array to be length 1 but that is not guaranteed by the API.
AutoTArray<RefPtr<ITypeInfo>, 1> typeInfos;
// Grab aInterface's ITypeInfo so that we may obtain information about its
// inheritance hierarchy.
RefPtr<ITypeInfo> typeInfo;
if (RegisteredProxy::Find(aInterface, getter_AddRefs(typeInfo))) {
typeInfos.AppendElement(std::move(typeInfo));
}
/**
* The main loop of this function searches the hierarchy of aInterface's
* parent interfaces, searching for aFrom.
*/
while (!typeInfos.IsEmpty()) {
RefPtr<ITypeInfo> curTypeInfo(typeInfos.PopLastElement());
TYPEATTR* typeAttr = nullptr;
HRESULT hr = curTypeInfo->GetTypeAttr(&typeAttr);
if (FAILED(hr)) {
break;
}
bool isFromParentVtable =
IsVtableIndexFromParentInterface(typeAttr, aVtableIndexHint);
WORD numParentInterfaces = typeAttr->cImplTypes;
curTypeInfo->ReleaseTypeAttr(typeAttr);
typeAttr = nullptr;
if (!isFromParentVtable) {
// The vtable index cannot belong to this interface (otherwise the IIDs
// would already have matched and we would have returned true). Since we
// now also know that the vtable index cannot possibly be contained inside
// curTypeInfo's parent interface, there is no point searching any further
// up the hierarchy from here. OTOH we still should check any remaining
// entries that are still in the typeInfos array, so we continue.
continue;
}
for (WORD i = 0; i < numParentInterfaces; ++i) {
HREFTYPE refCookie;
hr = curTypeInfo->GetRefTypeOfImplType(i, &refCookie);
if (FAILED(hr)) {
continue;
}
RefPtr<ITypeInfo> nextTypeInfo;
hr = curTypeInfo->GetRefTypeInfo(refCookie, getter_AddRefs(nextTypeInfo));
if (FAILED(hr)) {
continue;
}
hr = nextTypeInfo->GetTypeAttr(&typeAttr);
if (FAILED(hr)) {
continue;
}
IID nextIid = typeAttr->guid;
nextTypeInfo->ReleaseTypeAttr(typeAttr);
typeAttr = nullptr;
if (nextIid == aFrom) {
return true;
}
typeInfos.AppendElement(std::move(nextTypeInfo));
}
}
return false;
}
# endif // defined(MOZILLA_INTERNAL_API)
#endif // defined(ACCESSIBILITY)
#if defined(MOZILLA_INTERNAL_API)
bool IsClassThreadAwareInprocServer(REFCLSID aClsid) {
nsAutoString strClsid;

View File

@ -66,15 +66,6 @@ inline long BuildClsidPath(REFCLSID aClsid, wchar_t (&aPath)[N]) {
long CreateStream(const uint8_t* aBuf, const uint32_t aBufLen,
IStream** aOutStream);
/**
* Creates a deep copy of a proxy contained in a stream.
* @param aInStream Stream containing the proxy to copy. Its seek pointer must
* be positioned to point at the beginning of the proxy data.
* @param aOutStream Outparam to receive the newly created stream.
* @return HRESULT error code.
*/
long CopySerializedProxy(IStream* aInStream, IStream** aOutStream);
/**
* Length of a stringified GUID as formatted for the registry, i.e. including
* curly-braces and dashes.
@ -120,19 +111,6 @@ void GUIDToString(REFGUID aGuid,
wchar_t (&aOutBuf)[kGuidRegFormatCharLenInclNul]);
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
bool IsVtableIndexFromParentInterface(REFIID aInterface,
unsigned long aVtableIndex);
# if defined(MOZILLA_INTERNAL_API)
bool IsCallerExternalProcess();
bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
unsigned long aVtableIndexHint);
# endif // defined(MOZILLA_INTERNAL_API)
#endif // defined(ACCESSIBILITY)
/**
* Execute cleanup code when going out of scope if a condition is met.
* This is useful when, for example, particular cleanup needs to be performed

View File

@ -1,54 +0,0 @@
/* -*- 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 "VTableBuilder.h"
#include <stdlib.h>
static HRESULT STDMETHODCALLTYPE QueryInterfaceThunk(IUnknown* aThis,
REFIID aIid,
void** aOutInterface) {
void** table = (void**)aThis;
IUnknown* real = (IUnknown*)table[1];
return real->lpVtbl->QueryInterface(real, aIid, aOutInterface);
}
static ULONG STDMETHODCALLTYPE AddRefThunk(IUnknown* aThis) {
void** table = (void**)aThis;
IUnknown* real = (IUnknown*)table[1];
return real->lpVtbl->AddRef(real);
}
static ULONG STDMETHODCALLTYPE ReleaseThunk(IUnknown* aThis) {
void** table = (void**)aThis;
IUnknown* real = (IUnknown*)table[1];
return real->lpVtbl->Release(real);
}
IUnknown* BuildNullVTable(IUnknown* aUnk, uint32_t aVtblSize) {
void** table;
if (aVtblSize < 3) {
return NULL;
}
// We need to allocate slots for two additional pointers: The |lpVtbl|
// pointer, as well as |aUnk| which is needed to get |this| right when
// something calls through one of the IUnknown thunks.
table = calloc(aVtblSize + 2, sizeof(void*));
table[0] = &table[2]; // |lpVtbl|, points to the first entry of the vtable
table[1] = aUnk; // |this|
// Now the actual vtable entries for IUnknown
table[2] = &QueryInterfaceThunk;
table[3] = &AddRefThunk;
table[4] = &ReleaseThunk;
// Remaining entries are NULL thanks to calloc zero-initializing everything.
return (IUnknown*)table;
}
void DeleteNullVTable(IUnknown* aUnk) { free(aUnk); }

View File

@ -1,37 +0,0 @@
/* -*- 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_VTableBuilder_h
#define mozilla_mscom_VTableBuilder_h
#include <stdint.h>
#include <unknwn.h>
#if defined(__cplusplus)
extern "C" {
#endif
/**
* This function constructs an interface with |aVtblSize| entries, where the
* IUnknown methods are valid and redirect to |aUnk|. Other than the IUnknown
* methods, the resulting interface is not intended to actually be called; the
* remaining vtable entries are null pointers to enforce that.
*
* @param aUnk The IUnknown to which the null vtable will redirect its IUnknown
* methods.
* @param aVtblSize The total size of the vtable, including the IUnknown
* entries (thus this parameter must be >= 3).
* @return The resulting IUnknown, or nullptr on error.
*/
IUnknown* BuildNullVTable(IUnknown* aUnk, uint32_t aVtblSize);
void DeleteNullVTable(IUnknown* aUnk);
#if defined(__cplusplus)
}
#endif
#endif // mozilla_mscom_VTableBuilder_h

View File

@ -1,225 +0,0 @@
/* -*- 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/DebugOnly.h"
#include "mozilla/Mutex.h"
#include "nsThreadUtils.h"
#include "nsWindowsHelpers.h"
#include "nsProxyRelease.h"
static void InitializeCS(CRITICAL_SECTION& aCS) {
DWORD flags = 0;
#if defined(RELEASE_OR_BETA)
flags |= CRITICAL_SECTION_NO_DEBUG_INFO;
#endif
InitializeCriticalSectionEx(&aCS, 4000, flags);
}
namespace mozilla {
namespace mscom {
namespace detail {
SharedRef::SharedRef(WeakReferenceSupport* aSupport) : mSupport(aSupport) {
::InitializeCS(mCS);
}
SharedRef::~SharedRef() { ::DeleteCriticalSection(&mCS); }
void SharedRef::Lock() { ::EnterCriticalSection(&mCS); }
void SharedRef::Unlock() { ::LeaveCriticalSection(&mCS); }
HRESULT
SharedRef::ToStrongRef(IWeakReferenceSource** aOutStrongReference) {
RefPtr<IWeakReferenceSource> strongRef;
{ // Scope for lock
AutoCriticalSection lock(&mCS);
if (!mSupport) {
return E_POINTER;
}
strongRef = mSupport;
}
strongRef.forget(aOutStrongReference);
return S_OK;
}
HRESULT
SharedRef::Resolve(REFIID aIid, void** aOutStrongReference) {
RefPtr<WeakReferenceSupport> strongRef;
{ // Scope for lock
AutoCriticalSection lock(&mCS);
if (!mSupport) {
return E_POINTER;
}
strongRef = mSupport;
}
return strongRef->QueryInterface(aIid, aOutStrongReference);
}
void SharedRef::Clear() {
AutoCriticalSection lock(&mCS);
mSupport = nullptr;
}
} // namespace detail
typedef mozilla::detail::BaseAutoLock<detail::SharedRef&> SharedRefAutoLock;
typedef mozilla::detail::BaseAutoUnlock<detail::SharedRef&> SharedRefAutoUnlock;
WeakReferenceSupport::WeakReferenceSupport(Flags aFlags)
: mRefCnt(0), mFlags(aFlags) {
mSharedRef = new detail::SharedRef(this);
}
HRESULT
WeakReferenceSupport::QueryInterface(REFIID riid, void** ppv) {
RefPtr<IUnknown> punk;
if (!ppv) {
return E_INVALIDARG;
}
*ppv = nullptr;
// Raise the refcount for stabilization purposes during aggregation
StabilizeRefCount stabilize(*this);
if (riid == IID_IUnknown || riid == IID_IWeakReferenceSource) {
punk = static_cast<IUnknown*>(this);
} else {
HRESULT hr = WeakRefQueryInterface(riid, getter_AddRefs(punk));
if (FAILED(hr)) {
return hr;
}
}
if (!punk) {
return E_NOINTERFACE;
}
punk.forget(ppv);
return S_OK;
}
WeakReferenceSupport::StabilizeRefCount::StabilizeRefCount(
WeakReferenceSupport& aObject)
: mObject(aObject) {
SharedRefAutoLock lock(*mObject.mSharedRef);
++mObject.mRefCnt;
}
WeakReferenceSupport::StabilizeRefCount::~StabilizeRefCount() {
// We directly access these fields instead of calling Release() because we
// want to adjust the ref count without the other side effects (such as
// deleting this if the count drops back to zero, which may happen during
// an initial QI during object creation).
SharedRefAutoLock lock(*mObject.mSharedRef);
--mObject.mRefCnt;
}
ULONG
WeakReferenceSupport::AddRef() {
SharedRefAutoLock lock(*mSharedRef);
ULONG result = ++mRefCnt;
NS_LOG_ADDREF(this, result, "mscom::WeakReferenceSupport", sizeof(*this));
return result;
}
ULONG
WeakReferenceSupport::Release() {
ULONG newRefCnt;
{ // Scope for lock
SharedRefAutoLock lock(*mSharedRef);
newRefCnt = --mRefCnt;
if (newRefCnt == 0) {
mSharedRef->Clear();
}
}
NS_LOG_RELEASE(this, newRefCnt, "mscom::WeakReferenceSupport");
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("WeakReferenceSupport", self.forget());
}
}
return newRefCnt;
}
HRESULT
WeakReferenceSupport::GetWeakReference(IWeakReference** aOutWeakRef) {
if (!aOutWeakRef) {
return E_INVALIDARG;
}
RefPtr<WeakRef> weakRef = MakeAndAddRef<WeakRef>(mSharedRef);
return weakRef->QueryInterface(IID_IWeakReference, (void**)aOutWeakRef);
}
WeakRef::WeakRef(RefPtr<detail::SharedRef>& aSharedRef)
: mRefCnt(0), mSharedRef(aSharedRef) {
MOZ_ASSERT(aSharedRef);
}
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() {
ULONG result = ++mRefCnt;
NS_LOG_ADDREF(this, result, "mscom::WeakRef", sizeof(*this));
return result;
}
ULONG
WeakRef::Release() {
ULONG newRefCnt = --mRefCnt;
NS_LOG_RELEASE(this, newRefCnt, "mscom::WeakRef");
if (newRefCnt == 0) {
delete this;
}
return newRefCnt;
}
HRESULT
WeakRef::ToStrongRef(IWeakReferenceSource** aOutStrongReference) {
return mSharedRef->ToStrongRef(aOutStrongReference);
}
HRESULT
WeakRef::Resolve(REFIID aIid, void** aOutStrongReference) {
return mSharedRef->Resolve(aIid, aOutStrongReference);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,142 +0,0 @@
/* -*- 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/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/RefPtr.h"
#include "nsISupportsImpl.h"
/**
* Thread-safe weak references for COM that works pre-Windows 8 and do not
* require WinRT.
*/
namespace mozilla {
namespace mscom {
struct IWeakReferenceSource;
class WeakReferenceSupport;
namespace detail {
class SharedRef final {
public:
explicit SharedRef(WeakReferenceSupport* aSupport);
void Lock();
void Unlock();
HRESULT ToStrongRef(IWeakReferenceSource** aOutStringReference);
HRESULT Resolve(REFIID aIid, void** aOutStrongReference);
void Clear();
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedRef)
SharedRef(const SharedRef&) = delete;
SharedRef(SharedRef&&) = delete;
SharedRef& operator=(const SharedRef&) = delete;
SharedRef& operator=(SharedRef&&) = delete;
private:
~SharedRef();
private:
CRITICAL_SECTION mCS;
WeakReferenceSupport* mSupport;
};
} // namespace detail
// {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 ToStrongRef(
IWeakReferenceSource** aOutStrongReference) = 0;
virtual STDMETHODIMP Resolve(REFIID aIid, void** aOutStrongReference) = 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() = default;
virtual HRESULT WeakRefQueryInterface(REFIID aIid,
IUnknown** aOutInterface) = 0;
class MOZ_RAII StabilizeRefCount final {
public:
explicit StabilizeRefCount(WeakReferenceSupport& aObject);
~StabilizeRefCount();
StabilizeRefCount(const StabilizeRefCount&) = delete;
StabilizeRefCount(StabilizeRefCount&&) = delete;
StabilizeRefCount& operator=(const StabilizeRefCount&) = delete;
StabilizeRefCount& operator=(StabilizeRefCount&&) = delete;
private:
WeakReferenceSupport& mObject;
};
friend class StabilizeRefCount;
private:
RefPtr<detail::SharedRef> mSharedRef;
ULONG mRefCnt;
Flags mFlags;
};
class WeakRef final : public IWeakReference {
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IWeakReference
STDMETHODIMP ToStrongRef(IWeakReferenceSource** aOutStrongReference) override;
STDMETHODIMP Resolve(REFIID aIid, void** aOutStrongReference) override;
explicit WeakRef(RefPtr<detail::SharedRef>& aSharedRef);
private:
~WeakRef() = default;
Atomic<ULONG> mRefCnt;
RefPtr<detail::SharedRef> mSharedRef;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_WeakRef_h

View File

@ -8,16 +8,10 @@ EXPORTS.mozilla.mscom += [
"Aggregation.h",
"AgileReference.h",
"ApartmentRegion.h",
"AsyncInvoker.h",
"COMPtrHolder.h",
"COMWrappers.h",
"EnsureMTA.h",
"Objref.h",
"PassthruProxy.h",
"ProcessRuntime.h",
"ProfilerMarkers.h",
"ProxyStream.h",
"Ptr.h",
"Utils.h",
]
@ -25,67 +19,20 @@ DIRS += [
"mozglue",
]
SOURCES += [
"VTableBuilder.c",
]
UNIFIED_SOURCES += [
"AgileReference.cpp",
"COMWrappers.cpp",
"EnsureMTA.cpp",
"Objref.cpp",
"PassthruProxy.cpp",
"ProcessRuntime.cpp",
"ProfilerMarkers.cpp",
"ProxyStream.cpp",
"RegistrationAnnotator.cpp",
"Utils.cpp",
]
if CONFIG["ACCESSIBILITY"]:
DIRS += [
"oop",
]
EXPORTS.mozilla.mscom += [
"ActivationContext.h",
"DispatchForwarder.h",
"FastMarshaler.h",
"IHandlerProvider.h",
"Interceptor.h",
"InterceptorLog.h",
"MainThreadHandoff.h",
"MainThreadInvoker.h",
"Registration.h",
"SpinEvent.h",
"StructStream.h",
"WeakRef.h",
]
SOURCES += [
"Interceptor.cpp",
"MainThreadHandoff.cpp",
"Registration.cpp",
"SpinEvent.cpp",
"WeakRef.cpp",
]
UNIFIED_SOURCES += [
"ActivationContext.cpp",
"DispatchForwarder.cpp",
"FastMarshaler.cpp",
"InterceptorLog.cpp",
"MainThreadInvoker.cpp",
"StructStream.cpp",
]
LOCAL_INCLUDES += [
"/xpcom/base",
"/xpcom/build",
]
DEFINES["MOZ_MSCOM_REMARSHAL_NO_HANDLER"] = True
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul"

View File

@ -1,23 +0,0 @@
/* 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 ACT_CTX_RESOURCE_H
#define ACT_CTX_RESOURCE_H
#include <stdint.h>
#include <windows.h>
#include "mozilla/Types.h"
namespace mozilla {
namespace mscom {
struct ActCtxResource {
uint16_t mId;
HMODULE mModule;
};
} // namespace mscom
} // namespace mozilla
#endif

View File

@ -7,7 +7,6 @@
FINAL_LIBRARY = "mozglue"
EXPORTS.mozilla.mscom += [
"ActCtxResource.h",
"ProcessRuntimeShared.h",
]

View File

@ -1,142 +0,0 @@
/* -*- 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_Factory_h
#define mozilla_mscom_Factory_h
#if defined(MOZILLA_INTERNAL_API)
# error This code is NOT for internal Gecko use!
#endif // defined(MOZILLA_INTERNAL_API)
#include <objbase.h>
#include <unknwn.h>
#include <utility>
#include "Module.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
/* WARNING! The code in this file may be loaded into the address spaces of other
processes! It MUST NOT link against xul.dll or other Gecko binaries! Only
inline code may be included! */
namespace mozilla {
namespace mscom {
template <typename T>
class MOZ_NONHEAP_CLASS Factory : public IClassFactory {
template <typename... Args>
HRESULT DoCreate(Args&&... args) {
MOZ_DIAGNOSTIC_ASSERT(false, "This should not be executed");
return E_NOTIMPL;
}
template <typename... Args>
HRESULT DoCreate(HRESULT (*aFnPtr)(IUnknown*, REFIID, void**),
Args&&... args) {
return aFnPtr(std::forward<Args>(args)...);
}
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override {
if (!aOutInterface) {
return E_INVALIDARG;
}
if (aIid == IID_IUnknown || aIid == IID_IClassFactory) {
RefPtr<IClassFactory> punk(this);
punk.forget(aOutInterface);
return S_OK;
}
*aOutInterface = nullptr;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef() override {
Module::Lock();
return 2;
}
STDMETHODIMP_(ULONG) Release() override {
Module::Unlock();
return 1;
}
// IClassFactory
STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aIid,
void** aOutInterface) override {
return DoCreate(&T::Create, aOuter, aIid, aOutInterface);
}
STDMETHODIMP LockServer(BOOL aLock) override {
if (aLock) {
Module::Lock();
} else {
Module::Unlock();
}
return S_OK;
}
};
template <typename T>
class MOZ_NONHEAP_CLASS SingletonFactory : public Factory<T> {
public:
STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aIid,
void** aOutInterface) override {
if (aOuter || !aOutInterface) {
return E_INVALIDARG;
}
RefPtr<T> obj(sInstance);
if (!obj) {
obj = GetOrCreateSingleton();
}
return obj->QueryInterface(aIid, aOutInterface);
}
RefPtr<T> GetOrCreateSingleton() {
if (!sInstance) {
RefPtr<T> object;
if (FAILED(T::Create(getter_AddRefs(object)))) {
return nullptr;
}
sInstance = object.forget();
}
return sInstance;
}
RefPtr<T> GetSingleton() { return sInstance; }
void ClearSingleton() {
if (!sInstance) {
return;
}
DebugOnly<HRESULT> hr = ::CoDisconnectObject(sInstance.get(), 0);
MOZ_ASSERT(SUCCEEDED(hr));
sInstance = nullptr;
}
private:
static StaticRefPtr<T> sInstance;
};
template <typename T>
StaticRefPtr<T> SingletonFactory<T>::sInstance;
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Factory_h

View File

@ -1,281 +0,0 @@
/* -*- 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 "Handler.h"
#include "Module.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/mscom/Objref.h"
#include "nsWindowsHelpers.h"
#include <objbase.h>
#include <shlwapi.h>
#include <string.h>
/* WARNING! The code in this file may be loaded into the address spaces of other
processes! It MUST NOT link against xul.dll or other Gecko binaries! Only
inline code may be included! */
namespace mozilla {
namespace mscom {
Handler::Handler(IUnknown* aOuter, HRESULT* aResult)
: mRefCnt(0), mOuter(aOuter), mUnmarshal(nullptr), mHasPayload(false) {
MOZ_ASSERT(aResult);
if (!aOuter) {
*aResult = E_INVALIDARG;
return;
}
StabilizedRefCount<ULONG> stabilizer(mRefCnt);
*aResult =
::CoGetStdMarshalEx(aOuter, SMEXF_HANDLER, getter_AddRefs(mInnerUnk));
if (FAILED(*aResult)) {
return;
}
*aResult = mInnerUnk->QueryInterface(IID_IMarshal, (void**)&mUnmarshal);
if (FAILED(*aResult)) {
return;
}
// mUnmarshal is a weak ref
mUnmarshal->Release();
}
HRESULT
Handler::InternalQueryInterface(REFIID riid, void** ppv) {
if (!ppv) {
return E_INVALIDARG;
}
if (riid == IID_IUnknown) {
RefPtr<IUnknown> punk(static_cast<IUnknown*>(&mInternalUnknown));
punk.forget(ppv);
return S_OK;
}
if (riid == IID_IMarshal) {
RefPtr<IMarshal> ptr(this);
ptr.forget(ppv);
return S_OK;
}
// Try the handler implementation
HRESULT hr = QueryHandlerInterface(mInnerUnk, riid, ppv);
if (hr == S_FALSE) {
// The handler knows this interface is not available, so don't bother
// asking the proxy.
return E_NOINTERFACE;
}
if (hr != E_NOINTERFACE) {
return hr;
}
// Now forward to the marshaler's inner
return mInnerUnk->QueryInterface(riid, ppv);
}
ULONG
Handler::InternalAddRef() {
if (!mRefCnt) {
Module::Lock();
}
return ++mRefCnt;
}
ULONG
Handler::InternalRelease() {
ULONG newRefCnt = --mRefCnt;
if (newRefCnt == 0) {
delete this;
Module::Unlock();
}
return newRefCnt;
}
HRESULT
Handler::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags, CLSID* pCid) {
return mUnmarshal->GetUnmarshalClass(MarshalAs(riid), pv, dwDestContext,
pvDestContext, mshlflags, pCid);
}
HRESULT
Handler::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags, DWORD* pSize) {
if (!pSize) {
return E_INVALIDARG;
}
*pSize = 0;
RefPtr<IUnknown> unkToMarshal;
HRESULT hr;
REFIID marshalAs = MarshalAs(riid);
if (marshalAs == riid) {
unkToMarshal = static_cast<IUnknown*>(pv);
} else {
hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal));
if (FAILED(hr)) {
return hr;
}
}
// We do not necessarily want to use the pv that COM is giving us; we may want
// to marshal a different proxy that is more appropriate to what we're
// wrapping...
hr = mUnmarshal->GetMarshalSizeMax(marshalAs, unkToMarshal.get(),
dwDestContext, pvDestContext, mshlflags,
pSize);
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
return hr;
#else
if (FAILED(hr)) {
return hr;
}
if (!HasPayload()) {
return S_OK;
}
DWORD payloadSize = 0;
hr = GetHandlerPayloadSize(marshalAs, &payloadSize);
if (FAILED(hr)) {
return hr;
}
*pSize += payloadSize;
return S_OK;
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
}
HRESULT
Handler::GetMarshalInterface(REFIID aMarshalAsIid, NotNull<IUnknown*> aProxy,
NotNull<IID*> aOutIid,
NotNull<IUnknown**> aOutUnk) {
*aOutIid = aMarshalAsIid;
return aProxy->QueryInterface(
aMarshalAsIid,
reinterpret_cast<void**>(static_cast<IUnknown**>(aOutUnk)));
}
HRESULT
Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) {
// We do not necessarily want to use the pv that COM is giving us; we may want
// to marshal a different proxy that is more appropriate to what we're
// wrapping...
RefPtr<IUnknown> unkToMarshal;
HRESULT hr;
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
LARGE_INTEGER seekTo;
seekTo.QuadPart = 0;
ULARGE_INTEGER objrefPos;
// Save the current position as it points to the location where the OBJREF
// will be written.
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
if (FAILED(hr)) {
return hr;
}
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
REFIID marshalAs = MarshalAs(riid);
IID marshalOutAs;
hr = GetMarshalInterface(
marshalAs, WrapNotNull<IUnknown*>(mInnerUnk), WrapNotNull(&marshalOutAs),
WrapNotNull<IUnknown**>(getter_AddRefs(unkToMarshal)));
if (FAILED(hr)) {
return hr;
}
hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(),
dwDestContext, pvDestContext, mshlflags);
if (FAILED(hr)) {
return hr;
}
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
// Obtain the current stream position which is the end of the OBJREF
ULARGE_INTEGER endPos;
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &endPos);
if (FAILED(hr)) {
return hr;
}
// Now strip out the handler.
if (!StripHandlerFromOBJREF(WrapNotNull(pStm), objrefPos.QuadPart,
endPos.QuadPart)) {
return E_FAIL;
}
// Fix the IID
if (!SetIID(WrapNotNull(pStm), objrefPos.QuadPart, marshalOutAs)) {
return E_FAIL;
}
return S_OK;
#else
if (!HasPayload()) {
return S_OK;
}
// Unfortunately when COM re-marshals a proxy that prevouisly had a payload,
// we must re-serialize it.
return WriteHandlerPayload(pStm, marshalAs);
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
}
HRESULT
Handler::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
REFIID unmarshalAs = MarshalAs(riid);
HRESULT hr = mUnmarshal->UnmarshalInterface(pStm, unmarshalAs, ppv);
if (FAILED(hr)) {
return hr;
}
// This method may be called on the same object multiple times (as new
// interfaces are queried off the proxy). Not all interfaces will necessarily
// refresh the payload, so we set mHasPayload using OR to reflect that fact.
// (Otherwise mHasPayload could be cleared and the handler would think that
// it doesn't have a payload even though it actually does).
mHasPayload |= (ReadHandlerPayload(pStm, unmarshalAs) == S_OK);
return hr;
}
HRESULT
Handler::ReleaseMarshalData(IStream* pStm) {
return mUnmarshal->ReleaseMarshalData(pStm);
}
HRESULT
Handler::DisconnectObject(DWORD dwReserved) {
return mUnmarshal->DisconnectObject(dwReserved);
}
HRESULT
Handler::Unregister(REFCLSID aClsid) { return Module::Deregister(aClsid); }
HRESULT
Handler::Register(REFCLSID aClsid, const bool aMsixContainer) {
return Module::Register(aClsid, Module::ThreadingModel::DedicatedUiThreadOnly,
Module::ClassType::InprocHandler, nullptr,
aMsixContainer);
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,134 +0,0 @@
/* -*- 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_Handler_h
#define mozilla_mscom_Handler_h
#if defined(MOZILLA_INTERNAL_API)
# error This code is NOT for internal Gecko use!
#endif // defined(MOZILLA_INTERNAL_API)
#include <objidl.h>
#include "mozilla/mscom/Aggregation.h"
#include "mozilla/NotNull.h"
#include "mozilla/RefPtr.h"
/* WARNING! The code in this file may be loaded into the address spaces of other
processes! It MUST NOT link against xul.dll or other Gecko binaries! Only
inline code may be included! */
namespace mozilla {
namespace mscom {
class Handler : public IMarshal {
public:
// IMarshal
STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) override;
STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) override;
STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) override;
STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid,
void** ppv) override;
STDMETHODIMP ReleaseMarshalData(IStream* pStm) override;
STDMETHODIMP DisconnectObject(DWORD dwReserved) override;
/**
* This method allows the handler to return its own interfaces that override
* those interfaces that are exposed by the underlying COM proxy.
* @param aProxyUnknown is the IUnknown of the underlying COM proxy. This is
* provided to give the handler implementation an
* opportunity to acquire interfaces to the underlying
* remote object, if needed.
* @param aIid Interface requested, similar to IUnknown::QueryInterface
* @param aOutInterface Outparam for the resulting interface to return to the
* client.
* @return The usual HRESULT codes similarly to IUnknown::QueryInterface.
* If E_NOINTERFACE is returned, the proxy will be queried.
* If the handler is certain that this interface is not available,
* it can return S_FALSE to avoid querying the proxy. This will be
* translated to E_NOINTERFACE before it is returned to the client.
*/
virtual HRESULT QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
void** aOutInterface) = 0;
/**
* Called when the implementer should deserialize data in aStream.
* @return S_OK on success;
* S_FALSE if the deserialization was successful but there was no
* data; HRESULT error code otherwise.
*/
virtual HRESULT ReadHandlerPayload(IStream* aStream, REFIID aIid) {
return S_FALSE;
}
/**
* Unfortunately when COM marshals a proxy, it doesn't implicitly marshal
* the payload that was originally sent with the proxy. We must implement
* that code in the handler in order to make this happen.
*/
/**
* This function allows the implementer to substitute a different interface
* for marshaling than the one that COM is intending to marshal. For example,
* the implementer might want to marshal a proxy for an interface that is
* derived from the requested interface.
*
* The default implementation is the identity function.
*/
virtual REFIID MarshalAs(REFIID aRequestedIid) { return aRequestedIid; }
virtual HRESULT GetMarshalInterface(REFIID aMarshalAsIid,
NotNull<IUnknown*> aProxy,
NotNull<IID*> aOutIid,
NotNull<IUnknown**> aOutUnk);
/**
* Called when the implementer must provide the size of the payload.
*/
virtual HRESULT GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) {
if (!aOutPayloadSize) {
return E_INVALIDARG;
}
*aOutPayloadSize = 0;
return S_OK;
}
/**
* Called when the implementer should serialize the payload data into aStream.
*/
virtual HRESULT WriteHandlerPayload(IStream* aStream, REFIID aIid) {
return S_OK;
}
IUnknown* GetProxy() const { return mInnerUnk; }
static HRESULT Register(REFCLSID aClsid, const bool aMsixContainer = false);
static HRESULT Unregister(REFCLSID aClsid);
protected:
Handler(IUnknown* aOuter, HRESULT* aResult);
virtual ~Handler() {}
bool HasPayload() const { return mHasPayload; }
IUnknown* GetOuter() const { return mOuter; }
private:
ULONG mRefCnt;
IUnknown* mOuter;
RefPtr<IUnknown> mInnerUnk;
IMarshal* mUnmarshal; // WEAK
bool mHasPayload;
DECLARE_AGGREGATABLE(Handler);
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Handler_h

View File

@ -1,292 +0,0 @@
/* -*- 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 "Module.h"
#include <stdlib.h>
#include <ktmw32.h>
#include <memory.h>
#include <rpc.h>
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/Range.h"
#include "nsWindowsHelpers.h"
template <size_t N>
static const mozilla::Range<const wchar_t> LiteralToRange(
const wchar_t (&aArg)[N]) {
return mozilla::Range(aArg, N);
}
namespace mozilla {
namespace mscom {
ULONG Module::sRefCount = 0;
static const wchar_t* SubkeyNameFromClassType(
const Module::ClassType aClassType) {
switch (aClassType) {
case Module::ClassType::InprocServer:
return L"InprocServer32";
case Module::ClassType::InprocHandler:
return L"InprocHandler32";
default:
MOZ_CRASH("Unknown ClassType");
return nullptr;
}
}
static const Range<const wchar_t> ThreadingModelAsString(
const Module::ThreadingModel aThreadingModel) {
switch (aThreadingModel) {
case Module::ThreadingModel::DedicatedUiThreadOnly:
return LiteralToRange(L"Apartment");
case Module::ThreadingModel::MultiThreadedApartmentOnly:
return LiteralToRange(L"Free");
case Module::ThreadingModel::DedicatedUiThreadXorMultiThreadedApartment:
return LiteralToRange(L"Both");
case Module::ThreadingModel::AllThreadsAllApartments:
return LiteralToRange(L"Neutral");
default:
MOZ_CRASH("Unknown ThreadingModel");
return Range<const wchar_t>();
}
}
/* static */
HRESULT Module::Register(const CLSID* const* aClsids, const size_t aNumClsids,
const ThreadingModel aThreadingModel,
const ClassType aClassType, const GUID* const aAppId,
const bool aMsixContainer) {
MOZ_ASSERT(aClsids && aNumClsids);
if (!aClsids || !aNumClsids) {
return E_INVALIDARG;
}
MOZ_ASSERT(!aAppId || !aMsixContainer,
"aAppId isn't valid in an MSIX container");
const wchar_t* inprocName = SubkeyNameFromClassType(aClassType);
const Range<const wchar_t> threadingModelStr =
ThreadingModelAsString(aThreadingModel);
const DWORD threadingModelStrLenBytesInclNul =
threadingModelStr.length() * sizeof(wchar_t);
wchar_t strAppId[kGuidRegFormatCharLenInclNul] = {};
if (aAppId) {
GUIDToString(*aAppId, strAppId);
}
// Obtain the full path to this DLL
HMODULE thisModule;
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&Module::CanUnload),
&thisModule)) {
return HRESULT_FROM_WIN32(::GetLastError());
}
wchar_t absThisModulePath[MAX_PATH + 1] = {};
DWORD actualPathLenCharsExclNul = ::GetModuleFileNameW(
thisModule, absThisModulePath, ArrayLength(absThisModulePath));
if (!actualPathLenCharsExclNul ||
actualPathLenCharsExclNul == ArrayLength(absThisModulePath)) {
return HRESULT_FROM_WIN32(::GetLastError());
}
const DWORD actualPathLenBytesInclNul =
(actualPathLenCharsExclNul + 1) * sizeof(wchar_t);
nsAutoHandle txn;
// RegCreateKeyTransacted doesn't work in MSIX containers.
if (!aMsixContainer) {
// Use the name of this DLL as the name of the transaction
wchar_t txnName[_MAX_FNAME] = {};
if (_wsplitpath_s(absThisModulePath, nullptr, 0, nullptr, 0, txnName,
ArrayLength(txnName), nullptr, 0)) {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
// Manipulate the registry using a transaction so that any failures are
// rolled back.
txn.own(::CreateTransaction(nullptr, nullptr, TRANSACTION_DO_NOT_PROMOTE, 0,
0, 0, txnName));
if (txn.get() == INVALID_HANDLE_VALUE) {
return HRESULT_FROM_WIN32(::GetLastError());
}
}
HRESULT hr;
LSTATUS status;
// A single DLL may serve multiple components. For each CLSID, we register
// this DLL as its server and, when an AppId is specified, set up a reference
// from the CLSID to the specified AppId.
for (size_t idx = 0; idx < aNumClsids; ++idx) {
if (!aClsids[idx]) {
return E_INVALIDARG;
}
wchar_t clsidKeyPath[256];
hr = BuildClsidPath(*aClsids[idx], clsidKeyPath);
if (FAILED(hr)) {
return hr;
}
// Create the CLSID key
HKEY rawClsidKey;
// Subtle: If aMsixContainer is true, as well as calling a different
// function, we also use HKEY_CURRENT_USER. When false, we use
// HKEY_LOCAL_MACHINE.
if (aMsixContainer) {
status = ::RegCreateKeyExW(HKEY_CURRENT_USER, clsidKeyPath, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
nullptr, &rawClsidKey, nullptr);
} else {
status = ::RegCreateKeyTransactedW(
HKEY_LOCAL_MACHINE, clsidKeyPath, 0, nullptr, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, nullptr, &rawClsidKey, nullptr, txn, nullptr);
}
if (status != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(status);
}
nsAutoRegKey clsidKey(rawClsidKey);
if (aAppId) {
// This value associates the registered CLSID with the specified AppID
status = ::RegSetValueExW(clsidKey, L"AppID", 0, REG_SZ,
reinterpret_cast<const BYTE*>(strAppId),
ArrayLength(strAppId) * sizeof(wchar_t));
if (status != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(status);
}
}
HKEY rawInprocKey;
if (aMsixContainer) {
status = ::RegCreateKeyExW(clsidKey, inprocName, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
nullptr, &rawInprocKey, nullptr);
} else {
status = ::RegCreateKeyTransactedW(
clsidKey, inprocName, 0, nullptr, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, nullptr, &rawInprocKey, nullptr, txn, nullptr);
}
if (status != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(status);
}
nsAutoRegKey inprocKey(rawInprocKey);
// Set the component's path to this DLL
status = ::RegSetValueExW(inprocKey, nullptr, 0, REG_EXPAND_SZ,
reinterpret_cast<const BYTE*>(absThisModulePath),
actualPathLenBytesInclNul);
if (status != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(status);
}
status = ::RegSetValueExW(
inprocKey, L"ThreadingModel", 0, REG_SZ,
reinterpret_cast<const BYTE*>(threadingModelStr.begin().get()),
threadingModelStrLenBytesInclNul);
if (status != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(status);
}
}
if (aAppId) {
// When specified, we must also create a key for the AppID.
wchar_t appidKeyPath[256];
hr = BuildAppidPath(*aAppId, appidKeyPath);
if (FAILED(hr)) {
return hr;
}
HKEY rawAppidKey;
status = ::RegCreateKeyTransactedW(
HKEY_LOCAL_MACHINE, appidKeyPath, 0, nullptr, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, nullptr, &rawAppidKey, nullptr, txn, nullptr);
if (status != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(status);
}
nsAutoRegKey appidKey(rawAppidKey);
// Setting DllSurrogate to a null or empty string indicates to Windows that
// we want to use the default surrogate (i.e. dllhost.exe) to load our DLL.
status =
::RegSetValueExW(appidKey, L"DllSurrogate", 0, REG_SZ,
reinterpret_cast<const BYTE*>(L""), sizeof(wchar_t));
if (status != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(status);
}
}
if (!aMsixContainer) {
if (!::CommitTransaction(txn)) {
return HRESULT_FROM_WIN32(::GetLastError());
}
}
return S_OK;
}
/**
* Unfortunately the registry transaction APIs are not as well-developed for
* deleting things as they are for creating them. We just use RegDeleteTree
* for the implementation of this method.
*/
HRESULT Module::Deregister(const CLSID* const* aClsids, const size_t aNumClsids,
const GUID* const aAppId) {
MOZ_ASSERT(aClsids && aNumClsids);
if (!aClsids || !aNumClsids) {
return E_INVALIDARG;
}
HRESULT hr;
LSTATUS status;
// Delete the key for each CLSID. This will also delete any references to
// the AppId.
for (size_t idx = 0; idx < aNumClsids; ++idx) {
if (!aClsids[idx]) {
return E_INVALIDARG;
}
wchar_t clsidKeyPath[256];
hr = BuildClsidPath(*aClsids[idx], clsidKeyPath);
if (FAILED(hr)) {
return hr;
}
status = ::RegDeleteTreeW(HKEY_LOCAL_MACHINE, clsidKeyPath);
// We allow the deletion to succeed if the key was already gone
if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
return HRESULT_FROM_WIN32(status);
}
}
// Now delete the AppID key, if desired.
if (aAppId) {
wchar_t appidKeyPath[256];
hr = BuildAppidPath(*aAppId, appidKeyPath);
if (FAILED(hr)) {
return hr;
}
status = ::RegDeleteTreeW(HKEY_LOCAL_MACHINE, appidKeyPath);
// We allow the deletion to succeed if the key was already gone
if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
return HRESULT_FROM_WIN32(status);
}
}
return S_OK;
}
} // namespace mscom
} // namespace mozilla

View File

@ -1,98 +0,0 @@
/* -*- 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_Module_h
#define mozilla_mscom_Module_h
#if defined(MOZILLA_INTERNAL_API)
# error This code is NOT for internal Gecko use!
#endif // defined(MOZILLA_INTERNAL_API)
#include <objbase.h>
/* WARNING! The code in this file may be loaded into the address spaces of other
processes! It MUST NOT link against xul.dll or other Gecko binaries! Only
inline code may be included! */
namespace mozilla {
namespace mscom {
class Module {
public:
static HRESULT CanUnload() { return sRefCount == 0 ? S_OK : S_FALSE; }
static void Lock() { ++sRefCount; }
static void Unlock() { --sRefCount; }
enum class ThreadingModel {
DedicatedUiThreadOnly,
MultiThreadedApartmentOnly,
DedicatedUiThreadXorMultiThreadedApartment,
AllThreadsAllApartments,
};
enum class ClassType {
InprocServer,
InprocHandler,
};
/**
* In the Register functions, the aMsixContainer parameter specifies whether
* this registration is being performed inside an MSIX container. If true,
* the CLSID is registered in HKCU and a registry transaction is not used, as
* registry transactions don't work in an MSIX container. If false (the
* default), the CLSID is registered in HKLM and a registry transaction is
* used so that any failures roll back the entire operation. Specifying aAppId
* is only valid when aMsixContainer is false.
*/
static HRESULT Register(REFCLSID aClsid, const ThreadingModel aThreadingModel,
const ClassType aClassType = ClassType::InprocServer,
const GUID* const aAppId = nullptr,
const bool aMsixContainer = false) {
const CLSID* clsidArray[] = {&aClsid};
return Register(clsidArray, aThreadingModel, aClassType, aAppId,
aMsixContainer);
}
template <size_t N>
static HRESULT Register(const CLSID* (&aClsids)[N],
const ThreadingModel aThreadingModel,
const ClassType aClassType = ClassType::InprocServer,
const GUID* const aAppId = nullptr,
const bool aMsixContainer = false) {
return Register(aClsids, N, aThreadingModel, aClassType, aAppId,
aMsixContainer);
}
static HRESULT Deregister(REFCLSID aClsid,
const GUID* const aAppId = nullptr) {
const CLSID* clsidArray[] = {&aClsid};
return Deregister(clsidArray, aAppId);
}
template <size_t N>
static HRESULT Deregister(const CLSID* (&aClsids)[N],
const GUID* const aAppId = nullptr) {
return Deregister(aClsids, N, aAppId);
}
private:
static HRESULT Register(const CLSID* const* aClsids, const size_t aNumClsids,
const ThreadingModel aThreadingModel,
const ClassType aClassType, const GUID* const aAppId,
const bool aMsixContainer);
static HRESULT Deregister(const CLSID* const* aClsids,
const size_t aNumClsids, const GUID* const aAppId);
private:
static ULONG sRefCount;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Module_h

View File

@ -1,41 +0,0 @@
# -*- 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/.
Library("mscom_oop")
SOURCES += [
"../ActivationContext.cpp",
"../COMWrappers.cpp",
"../Objref.cpp",
"../Registration.cpp",
"../StructStream.cpp",
"../Utils.cpp",
]
UNIFIED_SOURCES += [
"Handler.cpp",
"Module.cpp",
]
OS_LIBS += [
"ktmw32",
"ole32",
"oleaut32",
"shlwapi",
]
LIBRARY_DEFINES["UNICODE"] = True
LIBRARY_DEFINES["_UNICODE"] = True
LIBRARY_DEFINES["MOZ_NO_MOZALLOC"] = True
LIBRARY_DEFINES["MOZ_MSCOM_REMARSHAL_NO_HANDLER"] = True
DisableStlWrapping()
NO_EXPAND_LIBS = True
FORCE_STATIC_LIB = True
# This DLL may be loaded into other processes, so we need static libs for
# Windows 7 and Windows 8.
USE_STATIC_LIBS = True

View File

@ -109,11 +109,6 @@ if CONFIG["OS_ARCH"] == "WINNT":
"PreXULSkeletonUI.cpp",
]
if CONFIG["ACCESSIBILITY"]:
SOURCES += [
"/ipc/mscom/ActivationContext.cpp",
]
elif CONFIG["HAVE_CLOCK_MONOTONIC"]:
SOURCES += [
"TimeStamp_posix.cpp",

View File

@ -178,12 +178,6 @@ ClassRegistrationInfoParent:
Microsoft COM class registration annotation for the parent process.
type: string
CoMarshalInterfaceFailure:
description: >
Annotation describing the error returned by trying to marshal an object
via CoMarshalInterface during the creation of an IPC proxy stream.
type: string
ContentSandboxCapabilities:
description: >
List of capabilities of the content process sandbox.
@ -214,12 +208,6 @@ GpuSandboxLevel:
GPU sandbox level.
type: integer
CoUnmarshalInterfaceResult:
description: >
Annotation describing the error returned by trying to unmarshal an object
via CoUnmarshalInterface during the creation of an IPC proxy stream.
type: integer
CPUMicrocodeVersion:
description: >
Version of the CPU microcode.
@ -237,12 +225,6 @@ CrashTime:
type: string
ping: true
CreateStreamOnHGlobalFailure:
description: >
Set when failing to obtain a global memory handle during the creation of an
IPC proxy stream.
type: string
CycleCollector:
description: >
Reason why the cycle collector crashed.
@ -304,12 +286,6 @@ FontName:
type: string
ping: true
GetHGlobalFromStreamFailure:
description: >
Error returned when invoking GetHGlobalFromStreamFailure() during the
creation of an IPC stream proxy.
type: string
GMPLibraryPath:
description: >
Holds the path to the GMP plugin library.
@ -420,16 +396,6 @@ InstallTime:
The time when Firefox was installed expressed as seconds since the Epoch
type: integer
InterfaceRegistrationInfoChild:
description: >
Microsoft COM interface registration annotation for the child process.
type: string
InterfaceRegistrationInfoParent:
description: >
Microsoft COM interface registration annotation for the parent process.
type: string
ipc_channel_error:
description: >
Set before a content process crashes because of an IPC channel error, holds
@ -708,28 +674,6 @@ ProfilerChildShutdownPhase:
type: string
ping: true
ProxyStreamSize:
description: >
Size of an IPC proxy stream.
type: integer
ProxyStreamSizeFrom:
description: >
Describes how the size of a proxy stream was obtained. It can be set to
either Stream::Stat or GlobalSize.
type: string
ProxyStreamUnmarshalStatus:
description: >
Status of the proxy stream unmarshalling, see ipc/mscom/ProxyStream.cpp for
the various value this annotation can take.
type: string
ProxyStreamValid:
description: >
Set to "false" when encountering an invalid IPC proxy stream.
type: string
PurgeablePhysicalMemory:
description: >
macOS only. Amount of physical memory currently allocated but which may
@ -834,12 +778,6 @@ StartupTime:
The time when Firefox was launched expressed in seconds since the Epoch.
type: integer
StatFailure:
description: >
Error returned when invoking IStream's Stat function during the creation
of an IPC proxy stream.
type: string
StorageConnectionNotClosed:
description: >
This annotation is added when a mozStorage connection has not been properly