Bug 1828719 - Remove omnijar Gradle project from srcdir r=geckoview-reviewers,nalexan...
[gecko.git] / ipc / mscom / Registration.cpp
blobe984ad0de4b29b466ffe5cc23e7b3669ec9bf0af
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // COM registration data structures are built with C code, so we need to
8 // simulate that in our C++ code by defining CINTERFACE before including
9 // anything else that could possibly pull in Windows header files.
10 #define CINTERFACE
12 #include "mozilla/mscom/Registration.h"
14 #include <utility>
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/StaticPtr.h"
20 #include "mozilla/Vector.h"
21 #include "mozilla/mscom/ActivationContext.h"
22 #include "mozilla/mscom/Utils.h"
23 #include "nsWindowsHelpers.h"
25 #if defined(MOZILLA_INTERNAL_API)
26 # include "mozilla/ClearOnShutdown.h"
27 # include "mozilla/mscom/EnsureMTA.h"
28 HRESULT RegisterPassthruProxy();
29 #else
30 # include <stdlib.h>
31 #endif // defined(MOZILLA_INTERNAL_API)
33 #include <oaidl.h>
34 #include <objidl.h>
35 #include <rpcproxy.h>
36 #include <shlwapi.h>
38 #include <algorithm>
40 /* This code MUST NOT use any non-inlined internal Mozilla APIs, as it will be
41 compiled into DLLs that COM may load into non-Mozilla processes! */
43 extern "C" {
45 // This function is defined in generated code for proxy DLLs but is not declared
46 // in rpcproxy.h, so we need this declaration.
47 void RPC_ENTRY GetProxyDllInfo(const ProxyFileInfo*** aInfo, const CLSID** aId);
50 namespace mozilla {
51 namespace mscom {
53 static bool GetContainingLibPath(wchar_t* aBuffer, size_t aBufferLen) {
54 HMODULE thisModule = reinterpret_cast<HMODULE>(GetContainingModuleHandle());
55 if (!thisModule) {
56 return false;
59 DWORD fileNameResult = GetModuleFileName(thisModule, aBuffer, aBufferLen);
60 if (!fileNameResult || (fileNameResult == aBufferLen &&
61 ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
62 return false;
65 return true;
68 static bool BuildLibPath(RegistrationFlags aFlags, wchar_t* aBuffer,
69 size_t aBufferLen, const wchar_t* aLeafName) {
70 if (aFlags == RegistrationFlags::eUseBinDirectory) {
71 if (!GetContainingLibPath(aBuffer, aBufferLen)) {
72 return false;
75 if (!PathRemoveFileSpec(aBuffer)) {
76 return false;
78 } else if (aFlags == RegistrationFlags::eUseSystemDirectory) {
79 UINT result = GetSystemDirectoryW(aBuffer, static_cast<UINT>(aBufferLen));
80 if (!result || result > aBufferLen) {
81 return false;
83 } else {
84 return false;
87 if (!PathAppend(aBuffer, aLeafName)) {
88 return false;
90 return true;
93 static bool RegisterPSClsids(const ProxyFileInfo** aProxyInfo,
94 const CLSID* aProxyClsid) {
95 while (*aProxyInfo) {
96 const ProxyFileInfo& curInfo = **aProxyInfo;
97 for (unsigned short idx = 0, size = curInfo.TableSize; idx < size; ++idx) {
98 HRESULT hr = CoRegisterPSClsid(*(curInfo.pStubVtblList[idx]->header.piid),
99 *aProxyClsid);
100 if (FAILED(hr)) {
101 return false;
104 ++aProxyInfo;
107 return true;
110 #if !defined(MOZILLA_INTERNAL_API)
111 using GetProxyDllInfoFnT = decltype(&GetProxyDllInfo);
113 static GetProxyDllInfoFnT ResolveGetProxyDllInfo() {
114 HMODULE thisModule = reinterpret_cast<HMODULE>(GetContainingModuleHandle());
115 if (!thisModule) {
116 return nullptr;
119 return reinterpret_cast<GetProxyDllInfoFnT>(
120 GetProcAddress(thisModule, "GetProxyDllInfo"));
122 #endif // !defined(MOZILLA_INTERNAL_API)
124 UniquePtr<RegisteredProxy> RegisterProxy() {
125 #if !defined(MOZILLA_INTERNAL_API)
126 GetProxyDllInfoFnT GetProxyDllInfoFn = ResolveGetProxyDllInfo();
127 MOZ_ASSERT(!!GetProxyDllInfoFn);
128 if (!GetProxyDllInfoFn) {
129 return nullptr;
131 #endif // !defined(MOZILLA_INTERNAL_API)
133 const ProxyFileInfo** proxyInfo = nullptr;
134 const CLSID* proxyClsid = nullptr;
135 #if defined(MOZILLA_INTERNAL_API)
136 GetProxyDllInfo(&proxyInfo, &proxyClsid);
137 #else
138 GetProxyDllInfoFn(&proxyInfo, &proxyClsid);
139 #endif // defined(MOZILLA_INTERNAL_API)
140 if (!proxyInfo || !proxyClsid) {
141 return nullptr;
144 IUnknown* classObject = nullptr;
145 HRESULT hr =
146 DllGetClassObject(*proxyClsid, IID_IUnknown, (void**)&classObject);
147 if (FAILED(hr)) {
148 return nullptr;
151 DWORD regCookie;
152 hr = CoRegisterClassObject(*proxyClsid, classObject, CLSCTX_INPROC_SERVER,
153 REGCLS_MULTIPLEUSE, &regCookie);
154 if (FAILED(hr)) {
155 classObject->lpVtbl->Release(classObject);
156 return nullptr;
159 wchar_t modulePathBuf[MAX_PATH + 1] = {0};
160 if (!GetContainingLibPath(modulePathBuf, ArrayLength(modulePathBuf))) {
161 CoRevokeClassObject(regCookie);
162 classObject->lpVtbl->Release(classObject);
163 return nullptr;
166 ITypeLib* typeLib = nullptr;
167 hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
168 MOZ_ASSERT(SUCCEEDED(hr));
169 if (FAILED(hr)) {
170 CoRevokeClassObject(regCookie);
171 classObject->lpVtbl->Release(classObject);
172 return nullptr;
175 #if defined(MOZILLA_INTERNAL_API)
176 hr = RegisterPassthruProxy();
177 MOZ_ASSERT(SUCCEEDED(hr));
178 if (FAILED(hr)) {
179 CoRevokeClassObject(regCookie);
180 classObject->lpVtbl->Release(classObject);
181 return nullptr;
183 #endif // defined(MOZILLA_INTERNAL_API)
185 // RegisteredProxy takes ownership of classObject and typeLib references
186 auto result(MakeUnique<RegisteredProxy>(classObject, regCookie, typeLib));
188 if (!RegisterPSClsids(proxyInfo, proxyClsid)) {
189 return nullptr;
192 return result;
195 UniquePtr<RegisteredProxy> RegisterProxy(const wchar_t* aLeafName,
196 RegistrationFlags aFlags) {
197 wchar_t modulePathBuf[MAX_PATH + 1] = {0};
198 if (!BuildLibPath(aFlags, modulePathBuf, ArrayLength(modulePathBuf),
199 aLeafName)) {
200 return nullptr;
203 nsModuleHandle proxyDll(LoadLibrary(modulePathBuf));
204 if (!proxyDll.get()) {
205 return nullptr;
208 // Instantiate an activation context so that CoGetClassObject will use any
209 // COM metadata embedded in proxyDll's manifest to resolve CLSIDs.
210 ActivationContextRegion actCtxRgn(proxyDll.get());
212 auto GetProxyDllInfoFn = reinterpret_cast<decltype(&GetProxyDllInfo)>(
213 GetProcAddress(proxyDll, "GetProxyDllInfo"));
214 if (!GetProxyDllInfoFn) {
215 return nullptr;
218 const ProxyFileInfo** proxyInfo = nullptr;
219 const CLSID* proxyClsid = nullptr;
220 GetProxyDllInfoFn(&proxyInfo, &proxyClsid);
221 if (!proxyInfo || !proxyClsid) {
222 return nullptr;
225 // We call CoGetClassObject instead of DllGetClassObject because it forces
226 // the COM runtime to manage the lifetime of the DLL.
227 IUnknown* classObject = nullptr;
228 HRESULT hr = CoGetClassObject(*proxyClsid, CLSCTX_INPROC_SERVER, nullptr,
229 IID_IUnknown, (void**)&classObject);
230 if (FAILED(hr)) {
231 return nullptr;
234 DWORD regCookie;
235 hr = CoRegisterClassObject(*proxyClsid, classObject, CLSCTX_INPROC_SERVER,
236 REGCLS_MULTIPLEUSE, &regCookie);
237 if (FAILED(hr)) {
238 classObject->lpVtbl->Release(classObject);
239 return nullptr;
242 ITypeLib* typeLib = nullptr;
243 hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
244 MOZ_ASSERT(SUCCEEDED(hr));
245 if (FAILED(hr)) {
246 CoRevokeClassObject(regCookie);
247 classObject->lpVtbl->Release(classObject);
248 return nullptr;
251 // RegisteredProxy takes ownership of proxyDll, classObject, and typeLib
252 // references
253 auto result(MakeUnique<RegisteredProxy>(
254 reinterpret_cast<uintptr_t>(proxyDll.disown()), classObject, regCookie,
255 typeLib));
257 if (!RegisterPSClsids(proxyInfo, proxyClsid)) {
258 return nullptr;
261 return result;
264 UniquePtr<RegisteredProxy> RegisterTypelib(const wchar_t* aLeafName,
265 RegistrationFlags aFlags) {
266 wchar_t modulePathBuf[MAX_PATH + 1] = {0};
267 if (!BuildLibPath(aFlags, modulePathBuf, ArrayLength(modulePathBuf),
268 aLeafName)) {
269 return nullptr;
272 ITypeLib* typeLib = nullptr;
273 HRESULT hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
274 if (FAILED(hr)) {
275 return nullptr;
278 // RegisteredProxy takes ownership of typeLib reference
279 auto result(MakeUnique<RegisteredProxy>(typeLib));
280 return result;
283 RegisteredProxy::RegisteredProxy(uintptr_t aModule, IUnknown* aClassObject,
284 uint32_t aRegCookie, ITypeLib* aTypeLib)
285 : mModule(aModule),
286 mClassObject(aClassObject),
287 mRegCookie(aRegCookie),
288 mTypeLib(aTypeLib)
289 #if defined(MOZILLA_INTERNAL_API)
291 mIsRegisteredInMTA(IsCurrentThreadMTA())
292 #endif // defined(MOZILLA_INTERNAL_API)
294 MOZ_ASSERT(aClassObject);
295 MOZ_ASSERT(aTypeLib);
296 AddToRegistry(this);
299 RegisteredProxy::RegisteredProxy(IUnknown* aClassObject, uint32_t aRegCookie,
300 ITypeLib* aTypeLib)
301 : mModule(0),
302 mClassObject(aClassObject),
303 mRegCookie(aRegCookie),
304 mTypeLib(aTypeLib)
305 #if defined(MOZILLA_INTERNAL_API)
307 mIsRegisteredInMTA(IsCurrentThreadMTA())
308 #endif // defined(MOZILLA_INTERNAL_API)
310 MOZ_ASSERT(aClassObject);
311 MOZ_ASSERT(aTypeLib);
312 AddToRegistry(this);
315 // If we're initializing from a typelib, it doesn't matter which apartment we
316 // run in, so mIsRegisteredInMTA may always be set to false in this case.
317 RegisteredProxy::RegisteredProxy(ITypeLib* aTypeLib)
318 : mModule(0),
319 mClassObject(nullptr),
320 mRegCookie(0),
321 mTypeLib(aTypeLib)
322 #if defined(MOZILLA_INTERNAL_API)
324 mIsRegisteredInMTA(false)
325 #endif // defined(MOZILLA_INTERNAL_API)
327 MOZ_ASSERT(aTypeLib);
328 AddToRegistry(this);
331 void RegisteredProxy::Clear() {
332 if (mTypeLib) {
333 mTypeLib->lpVtbl->Release(mTypeLib);
334 mTypeLib = nullptr;
336 if (mClassObject) {
337 // NB: mClassObject and mRegCookie must be freed from inside the apartment
338 // which they were created in.
339 auto cleanupFn = [&]() -> void {
340 ::CoRevokeClassObject(mRegCookie);
341 mRegCookie = 0;
342 mClassObject->lpVtbl->Release(mClassObject);
343 mClassObject = nullptr;
345 #if defined(MOZILLA_INTERNAL_API)
346 // This code only supports MTA when built internally
347 if (mIsRegisteredInMTA) {
348 EnsureMTA mta(cleanupFn);
349 } else {
350 cleanupFn();
352 #else
353 cleanupFn();
354 #endif // defined(MOZILLA_INTERNAL_API)
356 if (mModule) {
357 ::FreeLibrary(reinterpret_cast<HMODULE>(mModule));
358 mModule = 0;
362 RegisteredProxy::~RegisteredProxy() {
363 DeleteFromRegistry(this);
364 Clear();
367 RegisteredProxy::RegisteredProxy(RegisteredProxy&& aOther)
368 : mModule(0),
369 mClassObject(nullptr),
370 mRegCookie(0),
371 mTypeLib(nullptr)
372 #if defined(MOZILLA_INTERNAL_API)
374 mIsRegisteredInMTA(false)
375 #endif // defined(MOZILLA_INTERNAL_API)
377 *this = std::forward<RegisteredProxy>(aOther);
378 AddToRegistry(this);
381 RegisteredProxy& RegisteredProxy::operator=(RegisteredProxy&& aOther) {
382 Clear();
384 mModule = aOther.mModule;
385 aOther.mModule = 0;
386 mClassObject = aOther.mClassObject;
387 aOther.mClassObject = nullptr;
388 mRegCookie = aOther.mRegCookie;
389 aOther.mRegCookie = 0;
390 mTypeLib = aOther.mTypeLib;
391 aOther.mTypeLib = nullptr;
393 #if defined(MOZILLA_INTERNAL_API)
394 mIsRegisteredInMTA = aOther.mIsRegisteredInMTA;
395 #endif // defined(MOZILLA_INTERNAL_API)
397 return *this;
400 HRESULT
401 RegisteredProxy::GetTypeInfoForGuid(REFGUID aGuid,
402 ITypeInfo** aOutTypeInfo) const {
403 if (!aOutTypeInfo) {
404 return E_INVALIDARG;
406 if (!mTypeLib) {
407 return E_UNEXPECTED;
409 return mTypeLib->lpVtbl->GetTypeInfoOfGuid(mTypeLib, aGuid, aOutTypeInfo);
412 static StaticAutoPtr<Vector<RegisteredProxy*>> sRegistry;
414 namespace UseGetMutexForAccess {
416 // This must not be accessed directly; use GetMutex() instead
417 static CRITICAL_SECTION sMutex;
419 } // namespace UseGetMutexForAccess
421 static CRITICAL_SECTION* GetMutex() {
422 static CRITICAL_SECTION& mutex = []() -> CRITICAL_SECTION& {
423 #if defined(RELEASE_OR_BETA)
424 DWORD flags = CRITICAL_SECTION_NO_DEBUG_INFO;
425 #else
426 DWORD flags = 0;
427 #endif
428 InitializeCriticalSectionEx(&UseGetMutexForAccess::sMutex, 4000, flags);
429 #if !defined(MOZILLA_INTERNAL_API)
430 atexit([]() { DeleteCriticalSection(&UseGetMutexForAccess::sMutex); });
431 #endif
432 return UseGetMutexForAccess::sMutex;
433 }();
434 return &mutex;
437 /* static */
438 bool RegisteredProxy::Find(REFIID aIid, ITypeInfo** aTypeInfo) {
439 AutoCriticalSection lock(GetMutex());
441 if (!sRegistry) {
442 return false;
445 for (auto&& proxy : *sRegistry) {
446 if (SUCCEEDED(proxy->GetTypeInfoForGuid(aIid, aTypeInfo))) {
447 return true;
451 return false;
454 /* static */
455 void RegisteredProxy::AddToRegistry(RegisteredProxy* aProxy) {
456 MOZ_ASSERT(aProxy);
458 AutoCriticalSection lock(GetMutex());
460 if (!sRegistry) {
461 sRegistry = new Vector<RegisteredProxy*>();
463 #if !defined(MOZILLA_INTERNAL_API)
464 // sRegistry allocation is fallible outside of Mozilla processes
465 if (!sRegistry) {
466 return;
468 #endif
471 MOZ_ALWAYS_TRUE(sRegistry->emplaceBack(aProxy));
474 /* static */
475 void RegisteredProxy::DeleteFromRegistry(RegisteredProxy* aProxy) {
476 MOZ_ASSERT(aProxy);
478 AutoCriticalSection lock(GetMutex());
480 MOZ_ASSERT(sRegistry && !sRegistry->empty());
482 if (!sRegistry) {
483 return;
486 sRegistry->erase(std::remove(sRegistry->begin(), sRegistry->end(), aProxy),
487 sRegistry->end());
489 if (sRegistry->empty()) {
490 sRegistry = nullptr;
494 #if defined(MOZILLA_INTERNAL_API)
496 static StaticAutoPtr<Vector<std::pair<const ArrayData*, size_t>>> sArrayData;
498 void RegisterArrayData(const ArrayData* aArrayData, size_t aLength) {
499 AutoCriticalSection lock(GetMutex());
501 if (!sArrayData) {
502 sArrayData = new Vector<std::pair<const ArrayData*, size_t>>();
503 ClearOnShutdown(&sArrayData, ShutdownPhase::XPCOMShutdownThreads);
506 MOZ_ALWAYS_TRUE(sArrayData->emplaceBack(std::make_pair(aArrayData, aLength)));
509 const ArrayData* FindArrayData(REFIID aIid, ULONG aMethodIndex) {
510 AutoCriticalSection lock(GetMutex());
512 if (!sArrayData) {
513 return nullptr;
516 for (auto&& data : *sArrayData) {
517 for (size_t innerIdx = 0, innerLen = data.second; innerIdx < innerLen;
518 ++innerIdx) {
519 const ArrayData* array = data.first;
520 if (aMethodIndex == array[innerIdx].mMethodIndex &&
521 IsInterfaceEqualToOrInheritedFrom(aIid, array[innerIdx].mIid,
522 aMethodIndex)) {
523 return &array[innerIdx];
528 return nullptr;
531 #endif // defined(MOZILLA_INTERNAL_API)
533 } // namespace mscom
534 } // namespace mozilla