Bug 1303060: Additions to ipc/mscom for out-of-process components; r=jimm

MozReview-Commit-ID: IYjONGbBraG

--HG--
extra : rebase_source : a6eb90db7a9c21b91b9e763a15a9a661d41c5e77
This commit is contained in:
Aaron Klotz 2017-02-17 16:20:51 -07:00
Родитель af5bd498da
Коммит f3b557f70c
7 изменённых файлов: 719 добавлений и 0 удалений

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

@ -26,6 +26,10 @@ UNIFIED_SOURCES += [
]
if CONFIG['ACCESSIBILITY']:
DIRS += [
'oop',
]
EXPORTS.mozilla.mscom += [
'ActivationContext.h',
'DispatchForwarder.h',

155
ipc/mscom/oop/Factory.h Normal file
Просмотреть файл

@ -0,0 +1,155 @@
/* -*- 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 "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Move.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "Module.h"
#include <objbase.h>
#include <unknwn.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(mozilla::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

339
ipc/mscom/oop/Handler.cpp Normal file
Просмотреть файл

@ -0,0 +1,339 @@
/* -*- 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 "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)
{
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;
}
// mInnerMarshal 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 != 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()
{
if (--mRefCnt == 0) {
delete this;
Module::Unlock();
}
return mRefCnt;
}
HRESULT
Handler::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid)
{
return mUnmarshal->GetUnmarshalClass(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 (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;
}
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;
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;
}
}
hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(),
dwDestContext, pvDestContext, mshlflags);
if (FAILED(hr)) {
return hr;
}
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);
}
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;
}
hr = ReadHandlerPayload(pStm, unmarshalAs);
// 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 |= (hr == S_OK);
// hr may be S_FALSE, but we don't want to return that
return SUCCEEDED(hr) ? S_OK : hr;
}
HRESULT
Handler::ReleaseMarshalData(IStream* pStm)
{
return mUnmarshal->ReleaseMarshalData(pStm);
}
HRESULT
Handler::DisconnectObject(DWORD dwReserved)
{
return mUnmarshal->DisconnectObject(dwReserved);
}
template <size_t N>
static HRESULT
BuildClsidPath(wchar_t (&aPath)[N], REFCLSID aClsid)
{
const wchar_t kClsid[] = {L'C', L'L', L'S', L'I', L'D', L'\\'};
const size_t kReqdGuidLen = 39;
static_assert(N >= kReqdGuidLen + mozilla::ArrayLength(kClsid),
"aPath array is too short");
if (wcsncpy_s(aPath, kClsid, mozilla::ArrayLength(kClsid))) {
return E_INVALIDARG;
}
int guidConversionResult =
StringFromGUID2(aClsid, &aPath[mozilla::ArrayLength(kClsid)],
N - mozilla::ArrayLength(kClsid));
if (!guidConversionResult) {
return E_INVALIDARG;
}
return S_OK;
}
HRESULT
Handler::Unregister(REFCLSID aClsid)
{
wchar_t path[256] = {};
HRESULT hr = BuildClsidPath(path, aClsid);
if (FAILED(hr)) {
return hr;
}
hr = HRESULT_FROM_WIN32(SHDeleteKey(HKEY_CLASSES_ROOT, path));
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
HRESULT
Handler::Register(REFCLSID aClsid)
{
wchar_t path[256] = {};
HRESULT hr = BuildClsidPath(path, aClsid);
if (FAILED(hr)) {
return hr;
}
HKEY rawClsidKey;
DWORD disposition;
LONG result = RegCreateKeyEx(HKEY_CLASSES_ROOT, path, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
nullptr, &rawClsidKey, &disposition);
if (result != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(result);
}
nsAutoRegKey clsidKey(rawClsidKey);
if (wcscat_s(path, L"\\InprocHandler32")) {
return E_UNEXPECTED;
}
HKEY rawInprocHandlerKey;
result = RegCreateKeyEx(HKEY_CLASSES_ROOT, path, 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
nullptr, &rawInprocHandlerKey, &disposition);
if (result != ERROR_SUCCESS) {
Unregister(aClsid);
return HRESULT_FROM_WIN32(result);
}
nsAutoRegKey inprocHandlerKey(rawInprocHandlerKey);
wchar_t absLibPath[MAX_PATH + 1] = {};
HMODULE thisModule;
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCTSTR>(&Handler::Register),
&thisModule)) {
return HRESULT_FROM_WIN32(GetLastError());
}
DWORD size = GetModuleFileName(thisModule, absLibPath,
mozilla::ArrayLength(absLibPath));
if (!size || (size == mozilla::ArrayLength(absLibPath) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
DWORD lastError = GetLastError();
Unregister(aClsid);
return HRESULT_FROM_WIN32(lastError);
}
result = RegSetValueEx(inprocHandlerKey, L"", 0, REG_EXPAND_SZ,
reinterpret_cast<const BYTE*>(absLibPath),
sizeof(absLibPath));
if (result != ERROR_SUCCESS) {
Unregister(aClsid);
return HRESULT_FROM_WIN32(result);
}
const wchar_t kApartment[] = L"Apartment";
result = RegSetValueEx(inprocHandlerKey, L"ThreadingModel", 0, REG_SZ,
reinterpret_cast<const BYTE*>(kApartment),
sizeof(kApartment));
if (result != ERROR_SUCCESS) {
Unregister(aClsid);
return HRESULT_FROM_WIN32(result);
}
return S_OK;
}
} // namespace mscom
} // namespace mozilla

127
ipc/mscom/oop/Handler.h Normal file
Просмотреть файл

@ -0,0 +1,127 @@
/* -*- 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/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
*/
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; }
/**
* 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);
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

19
ipc/mscom/oop/Module.cpp Normal file
Просмотреть файл

@ -0,0 +1,19 @@
/* -*- 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 <memory.h>
#include <rpc.h>
namespace mozilla {
namespace mscom {
ULONG Module::sRefCount = 0;
} // namespace mscom
} // namespace mozilla

39
ipc/mscom/oop/Module.h Normal file
Просмотреть файл

@ -0,0 +1,39 @@
/* -*- 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; }
private:
static ULONG sRefCount;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_Module_h

36
ipc/mscom/oop/moz.build Normal file
Просмотреть файл

@ -0,0 +1,36 @@
# -*- 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',
'../Registration.cpp',
'../StructStream.cpp',
]
UNIFIED_SOURCES += [
'Handler.cpp',
'Module.cpp',
]
OS_LIBS += [
'ole32',
'oleaut32',
'shlwapi',
]
LIBRARY_DEFINES['UNICODE'] = True
LIBRARY_DEFINES['_UNICODE'] = True
LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True
DISABLE_STL_WRAPPING = True
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