Bug 1303060: Additions to ipc/mscom for out-of-process components; r=jimm
☠☠ backed out by 1ac7781188d2 ☠ ☠
authorAaron Klotz <aklotz@mozilla.com>
Fri, 17 Feb 2017 16:20:51 -0700
changeset 390999 729a1b8c7e476cada52be64b5063c5f49444438c
parent 390998 604605ca738cab9e3b20cd2716e10c72ecd5b859
child 391000 1ac7781188d2977c0b0ab00746d67e24ca04e608
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1303060
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1303060: Additions to ipc/mscom for out-of-process components; r=jimm MozReview-Commit-ID: IYjONGbBraG
ipc/mscom/moz.build
ipc/mscom/oop/Factory.h
ipc/mscom/oop/Handler.cpp
ipc/mscom/oop/Handler.h
ipc/mscom/oop/Module.cpp
ipc/mscom/oop/Module.h
ipc/mscom/oop/moz.build
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -21,16 +21,20 @@ UNIFIED_SOURCES += [
     'AgileReference.cpp',
     'EnsureMTA.cpp',
     'MainThreadRuntime.cpp',
     'ProxyStream.cpp',
     'Utils.cpp',
 ]
 
 if CONFIG['ACCESSIBILITY']:
+    DIRS += [
+        'oop',
+    ]
+
     EXPORTS.mozilla.mscom += [
         'ActivationContext.h',
         'DispatchForwarder.h',
         'IHandlerPayload.h',
         'Interceptor.h',
         'InterceptorLog.h',
         'MainThreadHandoff.h',
         'MainThreadInvoker.h',
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Factory.h
@@ -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
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Handler.cpp
@@ -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
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Handler.h
@@ -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
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Module.cpp
@@ -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
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Module.h
@@ -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
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/moz.build
@@ -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