Bug 1393589: Refactor NOPING marshaling into its own class, mscom::FastMarshaler, and use it with IGeckoBackChannel; r=jimm

MozReview-Commit-ID: 9osDoYcvtWV

--HG--
extra : rebase_source : 9ead42f1b0c79f11b15121274a996d33d7ad4830
This commit is contained in:
Aaron Klotz 2017-08-17 15:54:28 -06:00
Родитель d7d645f5c4
Коммит 5921c2115b
9 изменённых файлов: 334 добавлений и 86 удалений

Просмотреть файл

@ -16,6 +16,7 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Move.h"
#include "mozilla/mscom/AgileReference.h"
#include "mozilla/mscom/FastMarshaler.h"
#include "mozilla/mscom/MainThreadInvoker.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/mscom/StructStream.h"
@ -43,18 +44,25 @@ HandlerProvider::QueryInterface(REFIID riid, void** ppv)
return E_INVALIDARG;
}
RefPtr<IUnknown> punk;
if (riid == IID_IUnknown || riid == IID_IGeckoBackChannel) {
punk = static_cast<IGeckoBackChannel*>(this);
RefPtr<IUnknown> punk(static_cast<IGeckoBackChannel*>(this));
punk.forget(ppv);
return S_OK;
}
if (!punk) {
return E_NOINTERFACE;
if (riid == IID_IMarshal) {
if (!mFastMarshalUnk) {
HRESULT hr = mscom::FastMarshaler::Create(
static_cast<IGeckoBackChannel*>(this), getter_AddRefs(mFastMarshalUnk));
if (FAILED(hr)) {
return hr;
}
}
return mFastMarshalUnk->QueryInterface(riid, ppv);
}
punk.forget(ppv);
return S_OK;
return E_NOINTERFACE;
}
ULONG

Просмотреть файл

@ -67,6 +67,7 @@ private:
const IID mTargetUnkIid;
mscom::InterceptorTargetPtr<IUnknown> mTargetUnk; // Constant, main thread only
UniquePtr<mscom::StructToStream> mSerializer;
RefPtr<IUnknown> mFastMarshalUnk;
};
} // namespace a11y

190
ipc/mscom/FastMarshaler.cpp Normal file
Просмотреть файл

@ -0,0 +1,190 @@
/* -*- 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;
}
if (!IsCallerExternalProcess()) {
return aMshlFlags;
}
// The caller is our parent main thread. Disable ping functionality.
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

65
ipc/mscom/FastMarshaler.h Normal file
Просмотреть файл

@ -0,0 +1,65 @@
/* -*- 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 {
/**
* When we are marshaling to the parent process main thread, we want to turn
* off COM's ping functionality. That functionality is designed to free
* strong references held by defunct client processes. Since our content
* processes cannot outlive our parent process, we turn off pings when we know
* that the COM client is going to be our parent process. This provides a
* significant performance boost in a11y code due to large numbers of remote
* objects being created and destroyed within a short period of time.
*/
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

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Move.h"
#include "mozilla/mscom/DispatchForwarder.h"
#include "mozilla/mscom/FastMarshaler.h"
#include "mozilla/mscom/Interceptor.h"
#include "mozilla/mscom/InterceptorLog.h"
#include "mozilla/mscom/MainThreadInvoker.h"
@ -192,49 +193,13 @@ Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
}
/**
* When we are marshaling to the parent process main thread, we want to turn
* off COM's ping functionality. That functionality is designed to free
* strong references held by defunct client processes. Since our content
* processes cannot outlive our parent process, we turn off pings when we know
* that the COM client is going to be our parent process. This provides a
* significant performance boost in a11y code due to large numbers of remote
* objects being created and destroyed within a short period of time.
*/
DWORD
Interceptor::GetMarshalFlags(DWORD aDestContext, DWORD aMarshalFlags)
{
// Only worry about local contexts.
if (aDestContext != MSHCTX_LOCAL) {
return aMarshalFlags;
}
// Get the caller TID. We check for S_FALSE to ensure that the caller is a
// different process from ours, which is the only scenario we care about.
DWORD callerTid;
if (::CoGetCallerTID(&callerTid) != S_FALSE) {
return aMarshalFlags;
}
// Now we compare the caller TID to our parent main thread TID.
const DWORD chromeMainTid =
dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
if (callerTid != chromeMainTid) {
return aMarshalFlags;
}
// The caller is our parent main thread. Disable ping functionality.
return aMarshalFlags | MSHLFLAGS_NOPING;
}
HRESULT
Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid)
{
return mStdMarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext,
GetMarshalFlags(dwDestContext,
mshlflags), pCid);
mshlflags, pCid);
}
HRESULT
@ -243,10 +208,7 @@ Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
DWORD* pSize)
{
HRESULT hr = mStdMarshal->GetMarshalSizeMax(riid, pv, dwDestContext,
pvDestContext,
GetMarshalFlags(dwDestContext,
mshlflags),
pSize);
pvDestContext, mshlflags, pSize);
if (FAILED(hr)) {
return hr;
}
@ -285,44 +247,30 @@ Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
hr = mStdMarshal->MarshalInterface(pStm, riid, pv, dwDestContext,
pvDestContext,
GetMarshalFlags(dwDestContext, mshlflags));
pvDestContext, mshlflags);
if (FAILED(hr)) {
return hr;
}
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
if (XRE_IsContentProcess()) {
const DWORD chromeMainTid =
dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
if (XRE_IsContentProcess() && IsCallerExternalProcess()) {
// The caller isn't our chrome process, so do not provide a handler.
/*
* 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.
*/
DWORD callerTid;
if (::CoGetCallerTID(&callerTid) == S_FALSE && callerTid != chromeMainTid) {
// 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;
// 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)
@ -648,18 +596,24 @@ Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface)
}
if (aIid == IID_IMarshal) {
HRESULT hr;
if (!mStdMarshalUnk) {
HRESULT hr = ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource*>(this),
SMEXF_SERVER,
getter_AddRefs(mStdMarshalUnk));
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));
}
if (FAILED(hr)) {
return hr;
}
}
if (!mStdMarshal) {
HRESULT hr = mStdMarshalUnk->QueryInterface(IID_IMarshal,
(void**)&mStdMarshal);
hr = mStdMarshalUnk->QueryInterface(IID_IMarshal, (void**)&mStdMarshal);
if (FAILED(hr)) {
return hr;
}

Просмотреть файл

@ -130,8 +130,6 @@ private:
IUnknown** aOutInterface) override;
HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput);
static DWORD GetMarshalFlags(DWORD aDestContext, DWORD aMarshalFlags);
private:
InterceptorTargetPtr<IUnknown> mTarget;
RefPtr<IInterceptorSink> mEventSink;

Просмотреть файл

@ -4,6 +4,10 @@
* 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/. */
#if defined(MOZILLA_INTERNAL_API)
#include "mozilla/dom/ContentChild.h"
#endif
#if defined(ACCESSIBILITY)
#include "mozilla/mscom/Registration.h"
#if defined(MOZILLA_INTERNAL_API)
@ -238,6 +242,31 @@ GUIDToString(REFGUID aGuid, nsAString& aOutString)
}
}
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;
}
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)

Просмотреть файл

@ -48,6 +48,7 @@ uint32_t CopySerializedProxy(IStream* aInStream, IStream** aOutStream);
#if defined(MOZILLA_INTERNAL_API)
void GUIDToString(REFGUID aGuid, nsAString& aOutString);
bool IsCallerExternalProcess();
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)

Просмотреть файл

@ -11,6 +11,7 @@ EXPORTS.mozilla.mscom += [
'COMApartmentRegion.h',
'COMPtrHolder.h',
'EnsureMTA.h',
'FastMarshaler.h',
'MainThreadClientInfo.h',
'MainThreadRuntime.h',
'Objref.h',
@ -27,6 +28,7 @@ SOURCES += [
UNIFIED_SOURCES += [
'AgileReference.cpp',
'EnsureMTA.cpp',
'FastMarshaler.cpp',
'MainThreadClientInfo.cpp',
'MainThreadRuntime.cpp',
'Objref.cpp',