Bug 1339942: Make mscom registration able to be compiled and run outside Mozilla processes; r?jimm draft
authorAaron Klotz <aklotz@mozilla.com>
Wed, 15 Feb 2017 14:37:20 -0700
changeset 484864 8815b34127ad288c4bc5ea0600baca4e79e8e273
parent 484863 8ea321615c8a54b2c2c0d372de8145e4ff7187fb
child 545872 8428b142b13f3ff3a70671cea5c5cb4362b62ca3
push id45570
push useraklotz@mozilla.com
push dateWed, 15 Feb 2017 21:40:12 +0000
reviewersjimm
bugs1339942
milestone54.0a1
Bug 1339942: Make mscom registration able to be compiled and run outside Mozilla processes; r?jimm MozReview-Commit-ID: 3ETSE5Qn8nd
ipc/mscom/Registration.cpp
ipc/mscom/Registration.h
--- 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