Bug 1339942: Make mscom registration able to be compiled and run outside Mozilla processes; r?jimm
MozReview-Commit-ID: 3ETSE5Qn8nd
--- a/ipc/mscom/Registration.cpp
+++ b/ipc/mscom/Registration.cpp
@@ -5,35 +5,42 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// COM registration data structures are built with C code, so we need to
// simulate that in our C++ code by defining CINTERFACE before including
// anything else that could possibly pull in Windows header files.
#define CINTERFACE
#include "mozilla/mscom/ActivationContext.h"
-#include "mozilla/mscom/EnsureMTA.h"
#include "mozilla/mscom/Registration.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
-#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Move.h"
-#include "mozilla/Mutex.h"
#include "mozilla/Pair.h"
+#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
-#include "nsTArray.h"
+#include "mozilla/Vector.h"
#include "nsWindowsHelpers.h"
+#if defined(MOZILLA_INTERNAL_API)
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/mscom/EnsureMTA.h"
+#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);
@@ -269,44 +276,50 @@ RegisterTypelib(const wchar_t* aLeafName
}
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);
}
RegisteredProxy::~RegisteredProxy()
{
DeleteFromRegistry(this);
@@ -315,21 +328,26 @@ RegisteredProxy::~RegisteredProxy()
}
if (mClassObject) {
// NB: mClassObject and mRegCookie must be freed from inside the apartment
// which they were created in.
auto cleanupFn = [&]() -> void {
::CoRevokeClassObject(mRegCookie);
mClassObject->lpVtbl->Release(mClassObject);
};
+#if 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));
}
}
RegisteredProxy::RegisteredProxy(RegisteredProxy&& aOther)
{
@@ -358,91 +376,139 @@ RegisteredProxy::GetTypeInfoForInterface
return E_INVALIDARG;
}
if (!mTypeLib) {
return E_UNEXPECTED;
}
return mTypeLib->lpVtbl->GetTypeInfoOfGuid(mTypeLib, aIid, aOutTypeInfo);
}
-static StaticAutoPtr<nsTArray<RegisteredProxy*>> sRegistry;
-static StaticAutoPtr<Mutex> sRegMutex;
-static StaticAutoPtr<nsTArray<Pair<const ArrayData*, size_t>>> sArrayData;
+static StaticAutoPtr<Vector<RegisteredProxy*>> sRegistry;
+
+namespace UseGetMutexForAccess {
-static Mutex&
+// This must not be accessed directly; use GetMutex() instead
+static CRITICAL_SECTION sMutex;
+
+} // UseGetMutexForAccess
+
+static CRITICAL_SECTION*
GetMutex()
{
- static Mutex& mutex = []() -> Mutex& {
- if (!sRegMutex) {
- sRegMutex = new Mutex("RegisteredProxy::sRegMutex");
- ClearOnShutdown(&sRegMutex, ShutdownPhase::ShutdownThreads);
- }
- return *sRegMutex;
+ 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;
+ return &mutex;
}
/* static */ bool
RegisteredProxy::Find(REFIID aIid, ITypeInfo** aTypeInfo)
{
- MutexAutoLock lock(GetMutex());
- nsTArray<RegisteredProxy*>& registry = *sRegistry;
- for (uint32_t idx = 0, len = registry.Length(); idx < len; ++idx) {
- if (SUCCEEDED(registry[idx]->GetTypeInfoForInterface(aIid, aTypeInfo))) {
+ AutoCriticalSection lock(GetMutex());
+
+ if (!sRegistry) {
+ return false;
+ }
+
+ for (auto&& proxy : *sRegistry) {
+ if (SUCCEEDED(proxy->GetTypeInfoForInterface(aIid, aTypeInfo))) {
return true;
}
}
+
return false;
}
/* static */ void
RegisteredProxy::AddToRegistry(RegisteredProxy* aProxy)
{
- MutexAutoLock lock(GetMutex());
+ MOZ_ASSERT(aProxy);
+
+ AutoCriticalSection lock(GetMutex());
+
if (!sRegistry) {
- sRegistry = new nsTArray<RegisteredProxy*>();
- ClearOnShutdown(&sRegistry);
+ sRegistry = new Vector<RegisteredProxy*>();
+
+#if !defined(MOZILLA_INTERNAL_API)
+ // sRegistry allocation is fallible outside of Mozilla processes
+ if (!sRegistry) {
+ return;
+ }
+#endif
}
- sRegistry->AppendElement(aProxy);
+
+ sRegistry->emplaceBack(aProxy);
}
/* static */ void
RegisteredProxy::DeleteFromRegistry(RegisteredProxy* aProxy)
{
- MutexAutoLock lock(GetMutex());
- sRegistry->RemoveElement(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<Pair<const ArrayData*, size_t>>> sArrayData;
+
void
RegisterArrayData(const ArrayData* aArrayData, size_t aLength)
{
- MutexAutoLock lock(GetMutex());
+ AutoCriticalSection lock(GetMutex());
+
if (!sArrayData) {
- sArrayData = new nsTArray<Pair<const ArrayData*, size_t>>();
+ sArrayData = new Vector<Pair<const ArrayData*, size_t>>();
ClearOnShutdown(&sArrayData, ShutdownPhase::ShutdownThreads);
}
- sArrayData->AppendElement(MakePair(aArrayData, aLength));
+
+ sArrayData->emplaceBack(MakePair(aArrayData, aLength));
}
const ArrayData*
FindArrayData(REFIID aIid, ULONG aMethodIndex)
{
- MutexAutoLock lock(GetMutex());
+ AutoCriticalSection lock(GetMutex());
+
if (!sArrayData) {
return nullptr;
}
- for (uint32_t outerIdx = 0, outerLen = sArrayData->Length();
- outerIdx < outerLen; ++outerIdx) {
- auto& data = sArrayData->ElementAt(outerIdx);
+
+ for (auto&& data : *sArrayData) {
for (size_t innerIdx = 0, innerLen = data.second(); innerIdx < innerLen;
++innerIdx) {
const ArrayData* array = data.first();
if (aIid == array[innerIdx].mIid &&
aMethodIndex == array[innerIdx].mMethodIndex) {
return &array[innerIdx];
}
}
}
+
return nullptr;
}
+#endif // defined(MOZILLA_INTERNAL_API)
+
} // namespace mscom
} // namespace mozilla
--- a/ipc/mscom/Registration.h
+++ b/ipc/mscom/Registration.h
@@ -51,17 +51,19 @@ private:
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
};
@@ -73,16 +75,18 @@ UniquePtr<RegisteredProxy> RegisterProxy
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
@@ -139,13 +143,15 @@ 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