Bug 1390652: Part 3 - Add proxy wrapper that passes its inner proxy through content as a blob; r=jimm

MozReview-Commit-ID: A4pAyiuJUlz
This commit is contained in:
Aaron Klotz 2017-08-21 15:47:44 -06:00
parent 153e6759b5
commit d7d645f5c4
8 changed files with 669 additions and 8 deletions

View File

@ -15,6 +15,8 @@
#if defined(XP_WIN)
#include "AccessibleWrap.h"
#include "Compatibility.h"
#include "mozilla/mscom/PassthruProxy.h"
#include "mozilla/mscom/Ptr.h"
#include "nsWinUtils.h"
#include "RootAccessible.h"
#endif
@ -630,11 +632,12 @@ DocAccessibleParent::MaybeInitWindowEmulation()
SetEmulatedWindowHandle(aHwnd);
IAccessible* rawHWNDAcc = nullptr;
RefPtr<IAccessible> hwndAcc;
if (SUCCEEDED(::AccessibleObjectFromWindow(aHwnd, OBJID_WINDOW,
IID_IAccessible,
(void**)&rawHWNDAcc))) {
hWndAccHolder.Set(IDispatchHolder::COMPtrType(rawHWNDAcc));
getter_AddRefs(hwndAcc)))) {
RefPtr<IDispatch> wrapped(mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(hwndAcc)));
hWndAccHolder.Set(IDispatchHolder::COMPtrType(mscom::ToProxyUniquePtr(Move(wrapped))));
}
Unused << SendEmulatedWindow(reinterpret_cast<uintptr_t>(mEmulatedWindowHandle),
@ -668,11 +671,13 @@ DocAccessibleParent::SendParentCOMProxy()
return;
}
IAccessible* rawNative = nullptr;
outerDoc->GetNativeInterface((void**) &rawNative);
MOZ_ASSERT(rawNative);
RefPtr<IAccessible> nativeAcc;
outerDoc->GetNativeInterface(getter_AddRefs(nativeAcc));
MOZ_ASSERT(nativeAcc);
IDispatchHolder::COMPtrType ptr(rawNative);
RefPtr<IDispatch> wrapped(mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(nativeAcc)));
IDispatchHolder::COMPtrType ptr(mscom::ToProxyUniquePtr(Move(wrapped)));
IDispatchHolder holder(Move(ptr));
if (!PDocAccessibleParent::SendParentCOMProxy(holder)) {
return;

424
ipc/mscom/PassthruProxy.cpp Normal file
View File

@ -0,0 +1,424 @@
/* -*- 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(REFIID aIidToWrap, uint32_t aVTableSize,
NotNull<IUnknown*> aObjToWrap)
: mRefCnt(0)
, mWrappedIid(aIidToWrap)
, mVTableSize(aVTableSize)
, mVTable(nullptr)
, mForgetPreservedStream(false)
{
ProxyStream proxyStream(aIidToWrap, aObjToWrap,
ProxyStreamFlags::ePreservable);
mPreservedStream = Move(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();
}

113
ipc/mscom/PassthruProxy.h Normal file
View File

@ -0,0 +1,113 @@
/* -*- 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/NotNull.h"
#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");
RefPtr<PassthruProxy> passthru(new PassthruProxy(__uuidof(Iface),
detail::VTableSizer<Iface>::Size,
aIn));
RefPtr<Iface> result;
if (FAILED(passthru->QueryProxyInterface(getter_AddRefs(result)))) {
return nullptr;
}
return result;
}
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(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

@ -25,6 +25,7 @@
#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)
@ -152,6 +153,16 @@ RegisterProxy()
return nullptr;
}
#if defined(MOZILLA_INTERNAL_API)
hr = RegisterPassthruProxy();
MOZ_ASSERT(SUCCEEDED(hr));
if (FAILED(hr)) {
CoRevokeClassObject(regCookie);
classObject->lpVtbl->Release(classObject);
return nullptr;
}
#endif // defined(MOZILLA_INTERNAL_API)
// RegisteredProxy takes ownership of classObject and typeLib references
auto result(MakeUnique<RegisteredProxy>(classObject, regCookie, typeLib));

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- 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

65
ipc/mscom/VTableBuilder.c Normal file
View 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/. */
#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);
}

37
ipc/mscom/VTableBuilder.h Normal file
View File

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

@ -14,17 +14,23 @@ EXPORTS.mozilla.mscom += [
'MainThreadClientInfo.h',
'MainThreadRuntime.h',
'Objref.h',
'PassthruProxy.h',
'ProxyStream.h',
'Ptr.h',
'Utils.h',
]
SOURCES += [
'VTableBuilder.c',
]
UNIFIED_SOURCES += [
'AgileReference.cpp',
'EnsureMTA.cpp',
'MainThreadClientInfo.cpp',
'MainThreadRuntime.cpp',
'Objref.cpp',
'PassthruProxy.cpp',
'ProxyStream.cpp',
'Utils.cpp',
]