summaryrefslogtreecommitdiffstats
path: root/ipc/mscom/oop
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/mscom/oop')
-rw-r--r--ipc/mscom/oop/Factory.h142
-rw-r--r--ipc/mscom/oop/Handler.cpp281
-rw-r--r--ipc/mscom/oop/Handler.h134
-rw-r--r--ipc/mscom/oop/Module.cpp292
-rw-r--r--ipc/mscom/oop/Module.h98
-rw-r--r--ipc/mscom/oop/moz.build43
6 files changed, 990 insertions, 0 deletions
diff --git a/ipc/mscom/oop/Factory.h b/ipc/mscom/oop/Factory.h
new file mode 100644
index 0000000000..e95f1d2499
--- /dev/null
+++ b/ipc/mscom/oop/Factory.h
@@ -0,0 +1,142 @@
+/* -*- 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 <objbase.h>
+#include <unknwn.h>
+
+#include <utility>
+
+#include "Module.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPtr.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(std::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
diff --git a/ipc/mscom/oop/Handler.cpp b/ipc/mscom/oop/Handler.cpp
new file mode 100644
index 0000000000..1c40e5300f
--- /dev/null
+++ b/ipc/mscom/oop/Handler.cpp
@@ -0,0 +1,281 @@
+/* -*- 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 "mozilla/Assertions.h"
+#include "mozilla/mscom/Objref.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) {
+ MOZ_ASSERT(aResult);
+
+ 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;
+ }
+
+ // mUnmarshal 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 == S_FALSE) {
+ // The handler knows this interface is not available, so don't bother
+ // asking the proxy.
+ return E_NOINTERFACE;
+ }
+ 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() {
+ ULONG newRefCnt = --mRefCnt;
+ if (newRefCnt == 0) {
+ delete this;
+ Module::Unlock();
+ }
+ return newRefCnt;
+}
+
+HRESULT
+Handler::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
+ void* pvDestContext, DWORD mshlflags, CLSID* pCid) {
+ return mUnmarshal->GetUnmarshalClass(MarshalAs(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 defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+ return hr;
+#else
+ 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;
+#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+}
+
+HRESULT
+Handler::GetMarshalInterface(REFIID aMarshalAsIid, NotNull<IUnknown*> aProxy,
+ NotNull<IID*> aOutIid,
+ NotNull<IUnknown**> aOutUnk) {
+ *aOutIid = aMarshalAsIid;
+ return aProxy->QueryInterface(
+ aMarshalAsIid,
+ reinterpret_cast<void**>(static_cast<IUnknown**>(aOutUnk)));
+}
+
+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;
+
+#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+ LARGE_INTEGER seekTo;
+ seekTo.QuadPart = 0;
+
+ ULARGE_INTEGER objrefPos;
+
+ // Save the current position as it points to the location where the OBJREF
+ // will be written.
+ hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
+ if (FAILED(hr)) {
+ return hr;
+ }
+#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+
+ REFIID marshalAs = MarshalAs(riid);
+ IID marshalOutAs;
+
+ hr = GetMarshalInterface(
+ marshalAs, WrapNotNull<IUnknown*>(mInnerUnk), WrapNotNull(&marshalOutAs),
+ WrapNotNull<IUnknown**>(getter_AddRefs(unkToMarshal)));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(),
+ dwDestContext, pvDestContext, mshlflags);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+ // Obtain the current stream position which is the end of the OBJREF
+ ULARGE_INTEGER endPos;
+ hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &endPos);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Now strip out the handler.
+ if (!StripHandlerFromOBJREF(WrapNotNull(pStm), objrefPos.QuadPart,
+ endPos.QuadPart)) {
+ return E_FAIL;
+ }
+
+ // Fix the IID
+ if (!SetIID(WrapNotNull(pStm), objrefPos.QuadPart, marshalOutAs)) {
+ return E_FAIL;
+ }
+
+ return S_OK;
+#else
+ 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);
+#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
+}
+
+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;
+ }
+
+ // 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 |= (ReadHandlerPayload(pStm, unmarshalAs) == S_OK);
+
+ return hr;
+}
+
+HRESULT
+Handler::ReleaseMarshalData(IStream* pStm) {
+ return mUnmarshal->ReleaseMarshalData(pStm);
+}
+
+HRESULT
+Handler::DisconnectObject(DWORD dwReserved) {
+ return mUnmarshal->DisconnectObject(dwReserved);
+}
+
+HRESULT
+Handler::Unregister(REFCLSID aClsid) { return Module::Deregister(aClsid); }
+
+HRESULT
+Handler::Register(REFCLSID aClsid, const bool aMsixContainer) {
+ return Module::Register(aClsid, Module::ThreadingModel::DedicatedUiThreadOnly,
+ Module::ClassType::InprocHandler, nullptr,
+ aMsixContainer);
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/oop/Handler.h b/ipc/mscom/oop/Handler.h
new file mode 100644
index 0000000000..b22c925c22
--- /dev/null
+++ b/ipc/mscom/oop/Handler.h
@@ -0,0 +1,134 @@
+/* -*- 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/NotNull.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.
+ * If E_NOINTERFACE is returned, the proxy will be queried.
+ * If the handler is certain that this interface is not available,
+ * it can return S_FALSE to avoid querying the proxy. This will be
+ * translated to E_NOINTERFACE before it is returned to the client.
+ */
+ 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; }
+
+ virtual HRESULT GetMarshalInterface(REFIID aMarshalAsIid,
+ NotNull<IUnknown*> aProxy,
+ NotNull<IID*> aOutIid,
+ NotNull<IUnknown**> aOutUnk);
+
+ /**
+ * 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, const bool aMsixContainer = false);
+ 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
diff --git a/ipc/mscom/oop/Module.cpp b/ipc/mscom/oop/Module.cpp
new file mode 100644
index 0000000000..88ebf91531
--- /dev/null
+++ b/ipc/mscom/oop/Module.cpp
@@ -0,0 +1,292 @@
+/* -*- 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 <stdlib.h>
+
+#include <ktmw32.h>
+#include <memory.h>
+#include <rpc.h>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/mscom/Utils.h"
+#include "mozilla/Range.h"
+#include "nsWindowsHelpers.h"
+
+template <size_t N>
+static const mozilla::Range<const wchar_t> LiteralToRange(
+ const wchar_t (&aArg)[N]) {
+ return mozilla::Range(aArg, N);
+}
+
+namespace mozilla {
+namespace mscom {
+
+ULONG Module::sRefCount = 0;
+
+static const wchar_t* SubkeyNameFromClassType(
+ const Module::ClassType aClassType) {
+ switch (aClassType) {
+ case Module::ClassType::InprocServer:
+ return L"InprocServer32";
+ case Module::ClassType::InprocHandler:
+ return L"InprocHandler32";
+ default:
+ MOZ_CRASH("Unknown ClassType");
+ return nullptr;
+ }
+}
+
+static const Range<const wchar_t> ThreadingModelAsString(
+ const Module::ThreadingModel aThreadingModel) {
+ switch (aThreadingModel) {
+ case Module::ThreadingModel::DedicatedUiThreadOnly:
+ return LiteralToRange(L"Apartment");
+ case Module::ThreadingModel::MultiThreadedApartmentOnly:
+ return LiteralToRange(L"Free");
+ case Module::ThreadingModel::DedicatedUiThreadXorMultiThreadedApartment:
+ return LiteralToRange(L"Both");
+ case Module::ThreadingModel::AllThreadsAllApartments:
+ return LiteralToRange(L"Neutral");
+ default:
+ MOZ_CRASH("Unknown ThreadingModel");
+ return Range<const wchar_t>();
+ }
+}
+
+/* static */
+HRESULT Module::Register(const CLSID* const* aClsids, const size_t aNumClsids,
+ const ThreadingModel aThreadingModel,
+ const ClassType aClassType, const GUID* const aAppId,
+ const bool aMsixContainer) {
+ MOZ_ASSERT(aClsids && aNumClsids);
+ if (!aClsids || !aNumClsids) {
+ return E_INVALIDARG;
+ }
+ MOZ_ASSERT(!aAppId || !aMsixContainer,
+ "aAppId isn't valid in an MSIX container");
+
+ const wchar_t* inprocName = SubkeyNameFromClassType(aClassType);
+
+ const Range<const wchar_t> threadingModelStr =
+ ThreadingModelAsString(aThreadingModel);
+ const DWORD threadingModelStrLenBytesInclNul =
+ threadingModelStr.length() * sizeof(wchar_t);
+
+ wchar_t strAppId[kGuidRegFormatCharLenInclNul] = {};
+ if (aAppId) {
+ GUIDToString(*aAppId, strAppId);
+ }
+
+ // Obtain the full path to this DLL
+ HMODULE thisModule;
+ if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCWSTR>(&Module::CanUnload),
+ &thisModule)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ wchar_t absThisModulePath[MAX_PATH + 1] = {};
+ DWORD actualPathLenCharsExclNul = ::GetModuleFileNameW(
+ thisModule, absThisModulePath, ArrayLength(absThisModulePath));
+ if (!actualPathLenCharsExclNul ||
+ actualPathLenCharsExclNul == ArrayLength(absThisModulePath)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+ const DWORD actualPathLenBytesInclNul =
+ (actualPathLenCharsExclNul + 1) * sizeof(wchar_t);
+
+ nsAutoHandle txn;
+ // RegCreateKeyTransacted doesn't work in MSIX containers.
+ if (!aMsixContainer) {
+ // Use the name of this DLL as the name of the transaction
+ wchar_t txnName[_MAX_FNAME] = {};
+ if (_wsplitpath_s(absThisModulePath, nullptr, 0, nullptr, 0, txnName,
+ ArrayLength(txnName), nullptr, 0)) {
+ return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ }
+
+ // Manipulate the registry using a transaction so that any failures are
+ // rolled back.
+ txn.own(::CreateTransaction(nullptr, nullptr, TRANSACTION_DO_NOT_PROMOTE, 0,
+ 0, 0, txnName));
+ if (txn.get() == INVALID_HANDLE_VALUE) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+ }
+
+ HRESULT hr;
+ LSTATUS status;
+
+ // A single DLL may serve multiple components. For each CLSID, we register
+ // this DLL as its server and, when an AppId is specified, set up a reference
+ // from the CLSID to the specified AppId.
+ for (size_t idx = 0; idx < aNumClsids; ++idx) {
+ if (!aClsids[idx]) {
+ return E_INVALIDARG;
+ }
+
+ wchar_t clsidKeyPath[256];
+ hr = BuildClsidPath(*aClsids[idx], clsidKeyPath);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Create the CLSID key
+ HKEY rawClsidKey;
+ // Subtle: If aMsixContainer is true, as well as calling a different
+ // function, we also use HKEY_CURRENT_USER. When false, we use
+ // HKEY_LOCAL_MACHINE.
+ if (aMsixContainer) {
+ status = ::RegCreateKeyExW(HKEY_CURRENT_USER, clsidKeyPath, 0, nullptr,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ nullptr, &rawClsidKey, nullptr);
+ } else {
+ status = ::RegCreateKeyTransactedW(
+ HKEY_LOCAL_MACHINE, clsidKeyPath, 0, nullptr, REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS, nullptr, &rawClsidKey, nullptr, txn, nullptr);
+ }
+ if (status != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ nsAutoRegKey clsidKey(rawClsidKey);
+
+ if (aAppId) {
+ // This value associates the registered CLSID with the specified AppID
+ status = ::RegSetValueExW(clsidKey, L"AppID", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(strAppId),
+ ArrayLength(strAppId) * sizeof(wchar_t));
+ if (status != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ }
+
+ HKEY rawInprocKey;
+ if (aMsixContainer) {
+ status = ::RegCreateKeyExW(clsidKey, inprocName, 0, nullptr,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ nullptr, &rawInprocKey, nullptr);
+ } else {
+ status = ::RegCreateKeyTransactedW(
+ clsidKey, inprocName, 0, nullptr, REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS, nullptr, &rawInprocKey, nullptr, txn, nullptr);
+ }
+ if (status != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ nsAutoRegKey inprocKey(rawInprocKey);
+
+ // Set the component's path to this DLL
+ status = ::RegSetValueExW(inprocKey, nullptr, 0, REG_EXPAND_SZ,
+ reinterpret_cast<const BYTE*>(absThisModulePath),
+ actualPathLenBytesInclNul);
+ if (status != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(status);
+ }
+
+ status = ::RegSetValueExW(
+ inprocKey, L"ThreadingModel", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(threadingModelStr.begin().get()),
+ threadingModelStrLenBytesInclNul);
+ if (status != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ }
+
+ if (aAppId) {
+ // When specified, we must also create a key for the AppID.
+ wchar_t appidKeyPath[256];
+ hr = BuildAppidPath(*aAppId, appidKeyPath);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ HKEY rawAppidKey;
+ status = ::RegCreateKeyTransactedW(
+ HKEY_LOCAL_MACHINE, appidKeyPath, 0, nullptr, REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS, nullptr, &rawAppidKey, nullptr, txn, nullptr);
+ if (status != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ nsAutoRegKey appidKey(rawAppidKey);
+
+ // Setting DllSurrogate to a null or empty string indicates to Windows that
+ // we want to use the default surrogate (i.e. dllhost.exe) to load our DLL.
+ status =
+ ::RegSetValueExW(appidKey, L"DllSurrogate", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(L""), sizeof(wchar_t));
+ if (status != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ }
+
+ if (!aMsixContainer) {
+ if (!::CommitTransaction(txn)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+ }
+
+ return S_OK;
+}
+
+/**
+ * Unfortunately the registry transaction APIs are not as well-developed for
+ * deleting things as they are for creating them. We just use RegDeleteTree
+ * for the implementation of this method.
+ */
+HRESULT Module::Deregister(const CLSID* const* aClsids, const size_t aNumClsids,
+ const GUID* const aAppId) {
+ MOZ_ASSERT(aClsids && aNumClsids);
+ if (!aClsids || !aNumClsids) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr;
+ LSTATUS status;
+
+ // Delete the key for each CLSID. This will also delete any references to
+ // the AppId.
+ for (size_t idx = 0; idx < aNumClsids; ++idx) {
+ if (!aClsids[idx]) {
+ return E_INVALIDARG;
+ }
+
+ wchar_t clsidKeyPath[256];
+ hr = BuildClsidPath(*aClsids[idx], clsidKeyPath);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ status = ::RegDeleteTreeW(HKEY_LOCAL_MACHINE, clsidKeyPath);
+ // We allow the deletion to succeed if the key was already gone
+ if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ }
+
+ // Now delete the AppID key, if desired.
+ if (aAppId) {
+ wchar_t appidKeyPath[256];
+ hr = BuildAppidPath(*aAppId, appidKeyPath);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ status = ::RegDeleteTreeW(HKEY_LOCAL_MACHINE, appidKeyPath);
+ // We allow the deletion to succeed if the key was already gone
+ if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
+ return HRESULT_FROM_WIN32(status);
+ }
+ }
+
+ return S_OK;
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/oop/Module.h b/ipc/mscom/oop/Module.h
new file mode 100644
index 0000000000..1fa4b31c49
--- /dev/null
+++ b/ipc/mscom/oop/Module.h
@@ -0,0 +1,98 @@
+/* -*- 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; }
+
+ enum class ThreadingModel {
+ DedicatedUiThreadOnly,
+ MultiThreadedApartmentOnly,
+ DedicatedUiThreadXorMultiThreadedApartment,
+ AllThreadsAllApartments,
+ };
+
+ enum class ClassType {
+ InprocServer,
+ InprocHandler,
+ };
+
+ /**
+ * In the Register functions, the aMsixContainer parameter specifies whether
+ * this registration is being performed inside an MSIX container. If true,
+ * the CLSID is registered in HKCU and a registry transaction is not used, as
+ * registry transactions don't work in an MSIX container. If false (the
+ * default), the CLSID is registered in HKLM and a registry transaction is
+ * used so that any failures roll back the entire operation. Specifying aAppId
+ * is only valid when aMsixContainer is false.
+ */
+ static HRESULT Register(REFCLSID aClsid, const ThreadingModel aThreadingModel,
+ const ClassType aClassType = ClassType::InprocServer,
+ const GUID* const aAppId = nullptr,
+ const bool aMsixContainer = false) {
+ const CLSID* clsidArray[] = {&aClsid};
+ return Register(clsidArray, aThreadingModel, aClassType, aAppId,
+ aMsixContainer);
+ }
+
+ template <size_t N>
+ static HRESULT Register(const CLSID* (&aClsids)[N],
+ const ThreadingModel aThreadingModel,
+ const ClassType aClassType = ClassType::InprocServer,
+ const GUID* const aAppId = nullptr,
+ const bool aMsixContainer = false) {
+ return Register(aClsids, N, aThreadingModel, aClassType, aAppId,
+ aMsixContainer);
+ }
+
+ static HRESULT Deregister(REFCLSID aClsid,
+ const GUID* const aAppId = nullptr) {
+ const CLSID* clsidArray[] = {&aClsid};
+ return Deregister(clsidArray, aAppId);
+ }
+
+ template <size_t N>
+ static HRESULT Deregister(const CLSID* (&aClsids)[N],
+ const GUID* const aAppId = nullptr) {
+ return Deregister(aClsids, N, aAppId);
+ }
+
+ private:
+ static HRESULT Register(const CLSID* const* aClsids, const size_t aNumClsids,
+ const ThreadingModel aThreadingModel,
+ const ClassType aClassType, const GUID* const aAppId,
+ const bool aMsixContainer);
+
+ static HRESULT Deregister(const CLSID* const* aClsids,
+ const size_t aNumClsids, const GUID* const aAppId);
+
+ private:
+ static ULONG sRefCount;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_Module_h
diff --git a/ipc/mscom/oop/moz.build b/ipc/mscom/oop/moz.build
new file mode 100644
index 0000000000..5ead655e6f
--- /dev/null
+++ b/ipc/mscom/oop/moz.build
@@ -0,0 +1,43 @@
+# -*- 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",
+ "../COMWrappers.cpp",
+ "../Objref.cpp",
+ "../Registration.cpp",
+ "../StructStream.cpp",
+ "../Utils.cpp",
+]
+
+UNIFIED_SOURCES += [
+ "Handler.cpp",
+ "Module.cpp",
+]
+
+OS_LIBS += [
+ "ktmw32",
+ "ole32",
+ "oleaut32",
+ "shlwapi",
+]
+
+LIBRARY_DEFINES["UNICODE"] = True
+LIBRARY_DEFINES["_UNICODE"] = True
+LIBRARY_DEFINES["MOZ_NO_MOZALLOC"] = True
+LIBRARY_DEFINES["MOZ_MSCOM_REMARSHAL_NO_HANDLER"] = True
+
+DisableStlWrapping()
+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
+
+REQUIRES_UNIFIED_BUILD = True